diff options
author | Dan Williams <dcbw@redhat.com> | 2013-11-08 16:52:33 -0600 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2013-11-08 16:52:33 -0600 |
commit | 465458a206dcf7a568d784ff58efff4d138356eb (patch) | |
tree | a885a7f1a199ba15a9530744794d0106a0eead11 | |
parent | 6b6265ebebd7342a0d414bd1370e7af0e472d522 (diff) | |
parent | f4139112391794fd8f4bbf68988571065c96f443 (diff) | |
download | NetworkManager-465458a206dcf7a568d784ff58efff4d138356eb.tar.gz |
merge: better take-over of existing connections on NM startup (bgo #702488)
Read more state of connections that exist before NM starts, and take those
connections over more effectively.
https://bugzilla.gnome.org/show_bug.cgi?id=702488
41 files changed, 1585 insertions, 836 deletions
diff --git a/include/NetworkManager.h b/include/NetworkManager.h index 192f9a5178..46747a6529 100644 --- a/include/NetworkManager.h +++ b/include/NetworkManager.h @@ -548,6 +548,9 @@ typedef enum { /* DCB or FCoE setup failed */ NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED = 55, + /* teamd control failed */ + NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED = 56, + /* Unused */ NM_DEVICE_STATE_REASON_LAST = 0xFFFF } NMDeviceStateReason; diff --git a/introspection/nm-device.xml b/introspection/nm-device.xml index d9e7702d14..2332bdd874 100644 --- a/introspection/nm-device.xml +++ b/introspection/nm-device.xml @@ -592,6 +592,11 @@ DCB or FCoE setup failed. </tp:docstring> </tp:enumvalue> + <tp:enumvalue suffix="TEAMD_CONTROL_FAILED" value="56"> + <tp:docstring> + teamd control failed. + </tp:docstring> + </tp:enumvalue> </tp:enum> <tp:struct name="NM_DEVICE_STATE_REASON_STRUCT"> diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 923af8269d..8d9939b096 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -184,13 +184,6 @@ complete_connection (NMDevice *device, return TRUE; } -static gboolean -match_l2_config (NMDevice *self, NMConnection *connection) -{ - /* FIXME */ - return TRUE; -} - /******************************************************************/ static gboolean @@ -207,6 +200,57 @@ set_bond_attr (NMDevice *device, const char *attr, const char *value) return ret; } +/* Ignore certain bond options if they are zero (off/disabled) */ +static gboolean +ignore_if_zero (const char *option, const char *value) +{ + if (strcmp (option, "arp_interval") && + strcmp (option, "miimon") && + strcmp (option, "downdelay") && + strcmp (option, "updelay")) + return FALSE; + + return g_strcmp0 (value, "0") == 0 ? TRUE : FALSE; +} + +static void +update_connection (NMDevice *device, NMConnection *connection) +{ + NMSettingBond *s_bond = nm_connection_get_setting_bond (connection); + const char *ifname = nm_device_get_iface (device); + int ifindex = nm_device_get_ifindex (device); + const char **options; + + if (!s_bond) { + s_bond = (NMSettingBond *) nm_setting_bond_new (); + nm_connection_add_setting (connection, (NMSetting *) s_bond); + g_object_set (s_bond, NM_SETTING_BOND_INTERFACE_NAME, ifname, NULL); + } + + /* Read bond options from sysfs and update the Bond setting to match */ + options = nm_setting_bond_get_valid_options (s_bond); + while (options && *options) { + gs_free char *value = nm_platform_master_get_option (ifindex, *options); + const char *defvalue = nm_setting_bond_get_option_default (s_bond, *options); + + if (value && !ignore_if_zero (*options, value) && (g_strcmp0 (value, defvalue) != 0)) { + /* Replace " " with "," for arp_ip_targets from the kernel */ + if (strcmp (*options, "arp_ip_target") == 0) { + char *p = value; + + while (p && *p) { + if (*p == ' ') + *p = ','; + p++; + } + } + + nm_setting_bond_add_option (s_bond, *options, value); + } + options++; + } +} + static void set_arp_targets (NMDevice *device, const char *value, @@ -369,20 +413,23 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) } static gboolean -enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection) +enslave_slave (NMDevice *device, + NMDevice *slave, + NMConnection *connection, + gboolean configure) { - gboolean success, no_firmware = FALSE; + gboolean success = TRUE, no_firmware = FALSE; const char *iface = nm_device_get_ip_iface (device); const char *slave_iface = nm_device_get_ip_iface (slave); nm_device_master_check_slave_physical_port (device, slave, LOGD_BOND); - nm_device_take_down (slave, TRUE); - - success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device), - nm_device_get_ip_ifindex (slave)); - - nm_device_bring_up (slave, TRUE, &no_firmware); + if (configure) { + nm_device_take_down (slave, TRUE); + success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device), + nm_device_get_ip_ifindex (slave)); + nm_device_bring_up (slave, TRUE, &no_firmware); + } if (success) { nm_log_info (LOGD_BOND, "(%s): enslaved bond slave %s", iface, slave_iface); @@ -518,6 +565,8 @@ nm_device_bond_class_init (NMDeviceBondClass *klass) g_type_class_add_private (object_class, sizeof (NMDeviceBondPrivate)); + parent_class->connection_type = NM_SETTING_BOND_SETTING_NAME; + /* virtual methods */ object_class->constructed = constructed; object_class->get_property = get_property; @@ -529,7 +578,7 @@ nm_device_bond_class_init (NMDeviceBondClass *klass) parent_class->check_connection_available = check_connection_available; parent_class->complete_connection = complete_connection; - parent_class->match_l2_config = match_l2_config; + parent_class->update_connection = update_connection; parent_class->act_stage1_prepare = act_stage1_prepare; parent_class->enslave_slave = enslave_slave; diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 856eb83b3a..92cc9f2a77 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -24,6 +24,7 @@ #include <glib/gi18n.h> #include <netinet/ether.h> +#include <stdlib.h> #include "gsystem-local-alloc.h" #include "nm-device-bridge.h" @@ -180,13 +181,6 @@ complete_connection (NMDevice *device, return TRUE; } -static gboolean -match_l2_config (NMDevice *self, NMConnection *connection) -{ - /* FIXME */ - return TRUE; -} - /******************************************************************/ typedef struct { @@ -290,6 +284,80 @@ commit_slave_options (NMDevice *device, NMSettingBridgePort *setting) g_clear_object (&s_clear); } +static void +update_connection (NMDevice *device, NMConnection *connection) +{ + NMSettingBridge *s_bridge = nm_connection_get_setting_bridge (connection); + const char *ifname = nm_device_get_iface (device); + int ifindex = nm_device_get_ifindex (device); + const Option *option; + + if (!s_bridge) { + s_bridge = (NMSettingBridge *) nm_setting_bridge_new (); + nm_connection_add_setting (connection, (NMSetting *) s_bridge); + g_object_set (s_bridge, NM_SETTING_BRIDGE_INTERFACE_NAME, ifname, NULL); + } + + for (option = master_options; option->name; option++) { + gs_free char *str = nm_platform_master_get_option (ifindex, option->sysname); + int value = strtol (str, NULL, 10); + + /* See comments in set_sysfs_uint() about centiseconds. */ + if (option->user_hz_compensate) + value /= 100; + + g_object_set (s_bridge, option->name, value, NULL); + } +} + +/** + * nm_bridge_update_slave_connection: + * @slave: the slave #NMDevice, is *not* necessarily a bridge interface + * @connection: the #NMConnection to update with the bridge port settings + * + * Reads bridge port configuration and updates @connection with those + * properties. + * + * Returns: %TRUE if the port configuration was read and @connection updated, + * %FALSE if not. + */ +gboolean +nm_bridge_update_slave_connection (NMDevice *slave, NMConnection *connection) +{ + NMSettingBridgePort *s_port; + int ifindex = nm_device_get_ifindex (slave); + const Option *option; + + g_return_val_if_fail (NM_IS_DEVICE (slave), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + + s_port = nm_connection_get_setting_bridge_port (connection); + if (!s_port) { + s_port = (NMSettingBridgePort *) nm_setting_bridge_port_new (); + nm_connection_add_setting (connection, NM_SETTING (s_port)); + } + + for (option = slave_options; option->name; option++) { + gs_free char *str = nm_platform_slave_get_option (ifindex, option->sysname); + int value; + + if (str) { + value = strtol (str, NULL, 10); + + /* See comments in set_sysfs_uint() about centiseconds. */ + if (option->user_hz_compensate) + value /= 100; + + g_object_set (s_port, option->name, value, NULL); + } else { + nm_log_warn (LOGD_BRIDGE, "(%s): failed to read bridge port setting '%s'", + nm_device_get_iface (slave), option->sysname); + } + } + + return TRUE; +} + static NMActStageReturn act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) { @@ -308,12 +376,17 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) } static gboolean -enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection) +enslave_slave (NMDevice *device, + NMDevice *slave, + NMConnection *connection, + gboolean configure) { - if (!nm_platform_link_enslave (nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave))) - return FALSE; + if (configure) { + if (!nm_platform_link_enslave (nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave))) + return FALSE; - commit_slave_options (slave, nm_connection_get_setting_bridge_port (connection)); + commit_slave_options (slave, nm_connection_get_setting_bridge_port (connection)); + } nm_log_info (LOGD_BRIDGE, "(%s): attached bridge port %s", nm_device_get_ip_iface (device), @@ -439,6 +512,8 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass) g_type_class_add_private (object_class, sizeof (NMDeviceBridgePrivate)); + parent_class->connection_type = NM_SETTING_BRIDGE_SETTING_NAME; + /* virtual methods */ object_class->constructed = constructed; object_class->get_property = get_property; @@ -450,7 +525,7 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass) parent_class->check_connection_available = check_connection_available; parent_class->complete_connection = complete_connection; - parent_class->match_l2_config = match_l2_config; + parent_class->update_connection = update_connection; parent_class->act_stage1_prepare = act_stage1_prepare; parent_class->enslave_slave = enslave_slave; diff --git a/src/devices/nm-device-bridge.h b/src/devices/nm-device-bridge.h index 5570c73555..4194f5a411 100644 --- a/src/devices/nm-device-bridge.h +++ b/src/devices/nm-device-bridge.h @@ -57,6 +57,8 @@ GType nm_device_bridge_get_type (void); NMDevice *nm_device_bridge_new (NMPlatformLink *platform_device); NMDevice *nm_device_bridge_new_for_connection (NMConnection *connection); +gboolean nm_bridge_update_slave_connection (NMDevice *slave, NMConnection *connection); + G_END_DECLS #endif /* NM_DEVICE_BRIDGE_H */ diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 83f27b86b3..7b1c248eb8 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -1238,19 +1238,38 @@ spec_match_list (NMDevice *device, const GSList *specs) static void update_connection (NMDevice *device, NMConnection *connection) { + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device); NMSettingWired *s_wired = nm_connection_get_setting_wired (connection); guint maclen; gconstpointer mac = nm_device_get_hw_address (device, &maclen); + static const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + const char *mac_prop = NM_SETTING_WIRED_MAC_ADDRESS; + GByteArray *array; if (!s_wired) { s_wired = (NMSettingWired *) nm_setting_wired_new (); nm_connection_add_setting (connection, (NMSetting *) s_wired); } - if (mac && maclen == 6) { - GBytes *address = g_bytes_new (mac, maclen); + /* If the device reports a permanent address, use that for the MAC address + * and the current MAC, if different, is the cloned MAC. + */ + if (priv->perm_hw_addr && memcmp (priv->perm_hw_addr, null_mac, ETH_ALEN)) { + array = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (array, priv->perm_hw_addr, ETH_ALEN); + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, array, NULL); + g_byte_array_unref (array); + + mac_prop = NULL; + if (mac && memcmp (priv->perm_hw_addr, mac, ETH_ALEN)) + mac_prop = NM_SETTING_WIRED_CLONED_MAC_ADDRESS; + } - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, address, NULL); + if (mac_prop && mac && maclen == ETH_ALEN) { + array = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (array, (guint8 *) mac, maclen); + g_object_set (s_wired, mac_prop, array, NULL); + g_byte_array_unref (array); } /* We don't set the MTU as we don't know whether it was set explicitly */ diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c index f58c2ff79f..e435f5dff0 100644 --- a/src/devices/nm-device-generic.c +++ b/src/devices/nm-device-generic.c @@ -95,6 +95,13 @@ check_connection_compatible (NMDevice *device, return TRUE; } +static void +update_connection (NMDevice *device, NMConnection *connection) +{ + if (!nm_connection_get_setting_generic (connection)) + nm_connection_add_setting (connection, nm_setting_generic_new ()); +} + /**************************************************************/ NMDevice * @@ -184,6 +191,8 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass) g_type_class_add_private (klass, sizeof (NMDeviceGenericPrivate)); + parent_class->connection_type = NM_SETTING_GENERIC_SETTING_NAME; + object_class->constructed = constructed; object_class->dispose = dispose; object_class->get_property = get_property; @@ -191,6 +200,7 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass) parent_class->get_generic_capabilities = get_generic_capabilities; parent_class->check_connection_compatible = check_connection_compatible; + parent_class->update_connection = update_connection; /* properties */ g_object_class_install_property diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index b502fe70e2..1c91449e4e 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -307,11 +307,39 @@ complete_connection (NMDevice *device, return TRUE; } -static gboolean -match_l2_config (NMDevice *self, NMConnection *connection) +static void +update_connection (NMDevice *device, NMConnection *connection) { - /* FIXME */ - return TRUE; + NMSettingInfiniband *s_infiniband = nm_connection_get_setting_infiniband (connection); + guint maclen; + gconstpointer mac = nm_device_get_hw_address (device, &maclen); + static const guint8 null_mac[INFINIBAND_ALEN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + GByteArray *array; + char *mode_path, *contents; + const char *transport_mode = "datagram"; + + if (!s_infiniband) { + s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new (); + nm_connection_add_setting (connection, (NMSetting *) s_infiniband); + } + + if (mac && (maclen == INFINIBAND_ALEN) && (memcmp (mac, null_mac, maclen) != 0)) { + array = g_byte_array_sized_new (maclen); + g_byte_array_append (array, (guint8 *) mac, maclen); + g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, array, NULL); + g_byte_array_unref (array); + } + + mode_path = g_strdup_printf ("/sys/class/net/%s/mode", nm_device_get_iface (device)); + if (g_file_get_contents (mode_path, &contents, NULL, NULL)) { + if (strstr (contents, "datagram")) + transport_mode = "datagram"; + else if (strstr (contents, "connected")) + transport_mode = "connected"; + } + g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_TRANSPORT_MODE, transport_mode, NULL); + g_free (mode_path); + g_free (contents); } static gboolean @@ -384,11 +412,11 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass) parent_class->get_generic_capabilities = get_generic_capabilities; parent_class->check_connection_compatible = check_connection_compatible; parent_class->complete_connection = complete_connection; + parent_class->update_connection = update_connection; parent_class->spec_match_list = spec_match_list; parent_class->act_stage1_prepare = act_stage1_prepare; parent_class->ip4_config_pre_commit = ip4_config_pre_commit; - parent_class->match_l2_config = match_l2_config; /* properties */ diff --git a/src/devices/nm-device-team.c b/src/devices/nm-device-team.c index 1fe7caf927..8f119dd85f 100644 --- a/src/devices/nm-device-team.c +++ b/src/devices/nm-device-team.c @@ -31,6 +31,7 @@ #if WITH_TEAMDCTL #include <teamdctl.h> #endif +#include <stdlib.h> #include "nm-device-team.h" #include "nm-logging.h" @@ -195,11 +196,108 @@ complete_connection (NMDevice *device, return TRUE; } +#if WITH_TEAMDCTL static gboolean -match_l2_config (NMDevice *self, NMConnection *connection) +ensure_teamd_connection (NMDevice *self) { - /* FIXME */ - return TRUE; + NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self); + int err; + + if (priv->tdc) + return TRUE; + + priv->tdc = teamdctl_alloc (); + g_assert (priv->tdc); + err = teamdctl_connect (priv->tdc, nm_device_get_iface (self), NULL, NULL); + if (err) { + nm_log_err (LOGD_TEAM, "(%s): failed to connect to teamd", nm_device_get_iface (self)); + teamdctl_free (priv->tdc); + priv->tdc = NULL; + } + + return !!priv->tdc; +} +#endif + +static void +update_connection (NMDevice *device, NMConnection *connection) +{ + NMSettingTeam *s_team = nm_connection_get_setting_team (connection); + const char *iface = nm_device_get_iface (device); + + if (!s_team) { + s_team = (NMSettingTeam *) nm_setting_team_new (); + nm_connection_add_setting (connection, (NMSetting *) s_team); + g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_INTERFACE_NAME, iface, NULL); + } + +#if WITH_TEAMDCTL + if (ensure_teamd_connection (device)) { + char *config; + + config = teamdctl_config_get_raw (NM_DEVICE_TEAM_GET_PRIVATE (device)->tdc); + if (config) + g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_CONFIG, config, NULL); + else + nm_log_err (LOGD_TEAM, "(%s): failed to read teamd config", iface); + g_free (config); + } +#endif +} + +/******************************************************************/ + +gboolean +nm_team_update_slave_connection (NMDevice *slave, NMConnection *connection) +{ + NMSettingTeamPort *s_port; + const char *iface = nm_device_get_iface (slave); + char *port_config = NULL; + gboolean success = FALSE; +#if WITH_TEAMDCTL + const char *master_iface; + int master_ifindex; + struct teamdctl *tdc; + int err; +#endif + + g_return_val_if_fail (NM_IS_DEVICE (slave), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + +#if WITH_TEAMDCTL + master_ifindex = nm_platform_link_get_master (nm_device_get_ifindex (slave)); + g_assert (master_ifindex > 0); + master_iface = nm_platform_link_get_name (master_ifindex); + g_assert (master_iface); + + tdc = teamdctl_alloc (); + g_assert (tdc); + err = teamdctl_connect (tdc, master_iface, NULL, NULL); + if (err) { + nm_log_err (LOGD_TEAM, "(%s): failed to connect to teamd for master %s", + iface, master_iface); + teamdctl_free (tdc); + return FALSE; + } + /* FIXME: wait for libteamd to implement getting port config */ +/* port_config = teamdctl_port_config_get_raw (tdc, iface); */ + teamdctl_free (tdc); +#endif + + s_port = nm_connection_get_setting_team_port (connection); + if (!s_port) { + s_port = (NMSettingTeamPort *) nm_setting_team_port_new (); + nm_connection_add_setting (connection, NM_SETTING (s_port)); + } + + if (port_config) { + g_object_set (G_OBJECT (s_port), NM_SETTING_TEAM_PORT_CONFIG, port_config, NULL); + free (port_config); + success = TRUE; + } else + nm_log_err (LOGD_TEAM, "(%s): failed to read teamd port configuration", iface); + + return success; } /******************************************************************/ @@ -279,7 +377,7 @@ teamd_cleanup (NMDevice *dev, gboolean device_state_failed) if (device_state_failed) { if (nm_device_is_activating (dev) || (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED)) - nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NONE); + nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED); } } @@ -311,17 +409,9 @@ teamd_dbus_appeared (GDBusConnection *connection, nm_log_info (LOGD_TEAM, "(%s): teamd appeared on D-Bus", nm_device_get_iface (dev)); teamd_timeout_remove (dev); #if WITH_TEAMDCTL - if (!priv->tdc) { - int err; - - priv->tdc = teamdctl_alloc (); - g_assert (priv->tdc); - err = teamdctl_connect (priv->tdc, nm_device_get_iface (dev), NULL, NULL); - if (err) { - nm_log_err (LOGD_TEAM, "(%s): failed to connect to teamd", nm_device_get_iface (dev)); - teamdctl_free (priv->tdc); - priv->tdc = NULL; - } + if (!ensure_teamd_connection (dev)) { + nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED); + return; } #endif nm_device_activate_schedule_stage2_device_config (dev); @@ -548,48 +638,52 @@ deactivate (NMDevice *dev) } static gboolean -enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection) +enslave_slave (NMDevice *device, + NMDevice *slave, + NMConnection *connection, + gboolean configure) { #if WITH_TEAMDCTL NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device); #endif - gboolean success, no_firmware = FALSE; + gboolean success = TRUE, no_firmware = FALSE; const char *iface = nm_device_get_ip_iface (device); const char *slave_iface = nm_device_get_ip_iface (slave); NMSettingTeamPort *s_team_port; nm_device_master_check_slave_physical_port (device, slave, LOGD_TEAM); - nm_device_take_down (slave, TRUE); + if (configure) { + nm_device_take_down (slave, TRUE); - s_team_port = nm_connection_get_setting_team_port (connection); - if (s_team_port) { - const char *config = nm_setting_team_port_get_config (s_team_port); + s_team_port = nm_connection_get_setting_team_port (connection); + if (s_team_port) { + const char *config = nm_setting_team_port_get_config (s_team_port); - if (config) { + if (config) { #if WITH_TEAMDCTL - if (!priv->tdc) { - nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed, not connected to teamd", - iface, slave_iface); - } else { - int err; - - err = teamdctl_port_config_update_raw (priv->tdc, slave_iface, config); - if (err) { - nm_log_err (LOGD_TEAM, "(%s): failed to update config for port %s", iface, slave_iface); - return FALSE; + if (!priv->tdc) { + nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed, not connected to teamd", + iface, slave_iface); + } else { + int err; + + err = teamdctl_port_config_update_raw (priv->tdc, slave_iface, config); + if (err) { + nm_log_err (LOGD_TEAM, "(%s): failed to update config for port %s", iface, slave_iface); + return FALSE; + } } - } #else - nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed due to lack of Teamd control support", - iface, slave_iface); + nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed due to lack of Teamd control support", + iface, slave_iface); #endif + } } + success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device), + nm_device_get_ip_ifindex (slave)); + nm_device_bring_up (slave, TRUE, &no_firmware); } - success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device), - nm_device_get_ip_ifindex (slave)); - - nm_device_bring_up (slave, TRUE, &no_firmware); if (success) { nm_log_info (LOGD_TEAM, "(%s): enslaved team port %s", iface, slave_iface); @@ -744,8 +838,7 @@ nm_device_team_class_init (NMDeviceTeamClass *klass) parent_class->check_connection_compatible = check_connection_compatible; parent_class->check_connection_available = check_connection_available; parent_class->complete_connection = complete_connection; - - parent_class->match_l2_config = match_l2_config; + parent_class->update_connection = update_connection; parent_class->act_stage1_prepare = act_stage1_prepare; parent_class->deactivate = deactivate; diff --git a/src/devices/nm-device-team.h b/src/devices/nm-device-team.h index ebe5ae7d9f..fe1275c6b0 100644 --- a/src/devices/nm-device-team.h +++ b/src/devices/nm-device-team.h @@ -57,6 +57,8 @@ GType nm_device_team_get_type (void); NMDevice *nm_device_team_new (NMPlatformLink *platform_device); NMDevice *nm_device_team_new_for_connection (NMConnection *connection); +gboolean nm_team_update_slave_connection (NMDevice *slave, NMConnection *connection); + G_END_DECLS #endif /* NM_DEVICE_TEAM_H */ diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index ecd12f1857..ea74d65385 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -28,6 +28,7 @@ #include <netinet/ether.h> #include "nm-device-vlan.h" +#include "nm-manager.h" #include "nm-logging.h" #include "nm-utils.h" #include "NetworkManagerUtils.h" @@ -35,6 +36,7 @@ #include "nm-enum-types.h" #include "nm-dbus-manager.h" #include "nm-platform.h" +#include "nm-utils.h" #include "nm-device-vlan-glue.h" @@ -54,7 +56,7 @@ typedef struct { NMDevice *parent; guint parent_state_id; - guint vlan_id; + int vlan_id; } NMDeviceVlanPrivate; enum { @@ -274,35 +276,76 @@ complete_connection (NMDevice *device, return TRUE; } -static gboolean -match_l2_config (NMDevice *device, NMConnection *connection) +static void parent_state_changed (NMDevice *parent, NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data); + +static void +nm_device_vlan_set_parent (NMDeviceVlan *device, NMDevice *parent) { - NMSettingVlan *s_vlan; - gboolean fail_if_no_hwaddr = FALSE; + NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device); - s_vlan = nm_connection_get_setting_vlan (connection); - g_assert (s_vlan); + if (priv->parent_state_id) { + g_signal_handler_disconnect (priv->parent, priv->parent_state_id); + priv->parent_state_id = 0; + } + g_clear_object (&priv->parent); - if ( !nm_setting_vlan_get_parent (s_vlan) - && !nm_setting_vlan_get_interface_name (s_vlan)) { - /* If there's no parent and no interface name given, then the only way - * we have to identify the VLAN interface the connection matches is - * a hardware-specific setting's hardware address property, so we want - * to fail the match below if we there is none. - */ - fail_if_no_hwaddr = TRUE; + if (parent) { + priv->parent = g_object_ref (parent); + priv->parent_state_id = g_signal_connect (priv->parent, + "state-changed", + G_CALLBACK (parent_state_changed), + device); } + g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_PARENT); +} - /* MAC address check; we ask the parent to check our own MAC address, - * because only the parent knows what kind of NMSetting the MAC - * address will be in. The VLAN device shouldn't have to know what kind - * of interface the parent is. - */ - if (!match_hwaddr (device, connection, fail_if_no_hwaddr)) - return FALSE; +static void +update_connection (NMDevice *device, NMConnection *connection) +{ + NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device); + NMSettingVlan *s_vlan = nm_connection_get_setting_vlan (connection); + int ifindex = nm_device_get_ifindex (device); + int parent_ifindex = -1, vlan_id = -1; + NMDevice *parent; + const char *setting_parent, *new_parent; - /* FIXME: any more L2 checks? */ - return TRUE; + if (!s_vlan) { + s_vlan = (NMSettingVlan *) nm_setting_vlan_new (); + nm_connection_add_setting (connection, (NMSetting *) s_vlan); + g_object_set (s_vlan, NM_SETTING_VLAN_INTERFACE_NAME, nm_device_get_iface (device), NULL); + } + + nm_platform_vlan_get_info (ifindex, &parent_ifindex, &vlan_id); + if (priv->vlan_id != vlan_id) { + priv->vlan_id = vlan_id; + g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_ID); + } + + if (vlan_id != nm_setting_vlan_get_id (s_vlan)) + g_object_set (s_vlan, NM_SETTING_VLAN_ID, priv->vlan_id, NULL); + + parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex); + g_assert (parent); + if (priv->parent != parent) + nm_device_vlan_set_parent (NM_DEVICE_VLAN (device), parent); + + /* Update parent in the connection; default to parent's interface name */ + new_parent = nm_device_get_iface (parent); + setting_parent = nm_setting_vlan_get_parent (s_vlan); + if (setting_parent && nm_utils_is_uuid (setting_parent)) { + NMConnectionProvider *cp = nm_device_get_connection_provider (device); + NMConnection *parent_connection; + + /* Don't change a parent specified by UUID if it's still valid */ + parent_connection = nm_connection_provider_get_connection_by_uuid (cp, setting_parent); + if (parent_connection && nm_device_check_connection_compatible (parent, parent_connection, NULL)) + new_parent = NULL; + } + if (new_parent) + g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, new_parent, NULL); } static NMActStageReturn @@ -558,11 +601,7 @@ set_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_PARENT: - priv->parent = g_value_dup_object (value); - priv->parent_state_id = g_signal_connect (priv->parent, - "state-changed", - G_CALLBACK (parent_state_changed), - object); + nm_device_vlan_set_parent (NM_DEVICE_VLAN (object), g_value_get_object (value)); break; case PROP_VLAN_ID: priv->vlan_id = g_value_get_uint (value); @@ -585,8 +624,7 @@ dispose (GObject *object) } priv->disposed = TRUE; - g_signal_handler_disconnect (priv->parent, priv->parent_state_id); - g_object_unref (priv->parent); + nm_device_vlan_set_parent (self, NULL); G_OBJECT_CLASS (nm_device_vlan_parent_class)->dispose (object); } @@ -597,6 +635,8 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); + parent_class->connection_type = NM_SETTING_VLAN_SETTING_NAME; + g_type_class_add_private (object_class, sizeof (NMDeviceVlanPrivate)); /* virtual methods */ @@ -614,7 +654,7 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass) parent_class->check_connection_compatible = check_connection_compatible; parent_class->complete_connection = complete_connection; - parent_class->match_l2_config = match_l2_config; + parent_class->update_connection = update_connection; /* properties */ g_object_class_install_property diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 886d417eaa..1a0bcdca09 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -69,6 +69,10 @@ #include "nm-config.h" #include "nm-platform.h" +#include "nm-device-bridge.h" +#include "nm-device-bond.h" +#include "nm-device-team.h" + static void impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context); #include "nm-device-glue.h" @@ -164,6 +168,7 @@ typedef struct { typedef struct { NMDevice *slave; gboolean enslaved; + gboolean configure; guint watch_id; } SlaveInfo; @@ -349,7 +354,7 @@ static void link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink * static void check_carrier (NMDevice *device); static void nm_device_queued_ip_config_change_clear (NMDevice *self); -static void update_ip_config (NMDevice *self); +static void update_ip_config (NMDevice *self, gboolean capture_dhcp); static void device_ip_changed (NMPlatform *platform, int ifindex, gpointer platform_object, NMPlatformReason reason, gpointer user_data); static void nm_device_slave_notify_enslave (NMDevice *dev, gboolean success); @@ -545,7 +550,6 @@ constructor (GType type, update_accept_ra_save (dev); update_ip6_privacy_save (dev); - update_ip_config (dev); /* Watch for external IP config changes */ platform = nm_platform_get (); @@ -717,7 +721,10 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface) priv->ip_iface = g_strdup (iface); if (priv->ip_iface) { priv->ip_ifindex = nm_platform_link_get_ifindex (priv->ip_iface); - if (priv->ip_ifindex <= 0) { + if (priv->ip_ifindex > 0) { + if (!nm_platform_link_is_up (priv->ip_ifindex)) + nm_platform_link_set_up (priv->ip_ifindex); + } else { /* Device IP interface must always be a kernel network interface */ nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", iface); } @@ -955,7 +962,7 @@ nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connectio return FALSE; g_warn_if_fail (info->enslaved == FALSE); - success = NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave, connection); + success = NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave, connection, info->configure); info->enslaved = success; nm_device_slave_notify_enslave (info->slave, success); @@ -1242,6 +1249,8 @@ slave_state_changed (NMDevice *slave, * nm_device_master_add_slave: * @dev: the master device * @slave: the slave device to enslave + * @configure: pass %TRUE if the slave should be configured by the master, or + * %FALSE if it is already configured outside NetworkManager * * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team, * etc) then this function adds @slave to the slave list for later enslavement. @@ -1249,7 +1258,7 @@ slave_state_changed (NMDevice *slave, * Returns: %TRUE on success, %FALSE on failure */ gboolean -nm_device_master_add_slave (NMDevice *dev, NMDevice *slave) +nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); SlaveInfo *info; @@ -1262,6 +1271,7 @@ nm_device_master_add_slave (NMDevice *dev, NMDevice *slave) if (!find_slave_info (dev, slave)) { info = g_malloc0 (sizeof (SlaveInfo)); info->slave = g_object_ref (slave); + info->configure = configure; info->watch_id = g_signal_connect (slave, "state-changed", G_CALLBACK (slave_state_changed), dev); priv->slaves = g_slist_prepend (priv->slaves, info); @@ -1625,6 +1635,10 @@ device_has_config (NMDevice *device) if (nm_device_is_software (device)) return TRUE; + /* Slaves are also configured by definition */ + if (nm_platform_link_get_master (priv->ifindex) > 0) + return TRUE; + return FALSE; } @@ -1634,12 +1648,15 @@ nm_device_generate_connection (NMDevice *device) NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); const char *ifname = nm_device_get_iface (device); + int ifindex = nm_device_get_ifindex (device); NMConnection *connection; NMSetting *s_con; NMSetting *s_ip4; NMSetting *s_ip6; gs_free char *uuid = NULL; gs_free char *name = NULL; + int master_ifindex = 0; + const char *ip4_method, *ip6_method; /* If update_connection() is not implemented, just fail. */ if (!klass->update_connection) @@ -1649,12 +1666,8 @@ nm_device_generate_connection (NMDevice *device) if (!device_has_config (device)) return NULL; - nm_log_info (LOGD_DEVICE, "(%s): Generating connection from current device status.", ifname); - connection = nm_connection_new (); s_con = nm_setting_connection_new (); - s_ip4 = nm_setting_ip4_config_new (); - s_ip6 = nm_setting_ip6_config_new (); uuid = nm_utils_uuid_generate (); name = g_strdup_printf ("%s", ifname); @@ -1666,18 +1679,75 @@ nm_device_generate_connection (NMDevice *device) NULL); if (klass->connection_type) g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, klass->connection_type, NULL); - nm_ip4_config_update_setting (priv->ip4_config, (NMSettingIP4Config *) s_ip4); - nm_ip6_config_update_setting (priv->ip6_config, (NMSettingIP6Config *) s_ip6); - nm_connection_add_setting (connection, s_con); + + /* If the device is a slave, update various slave settings */ + if (ifindex) + master_ifindex = nm_platform_link_get_master (ifindex); + if (master_ifindex) { + const char *master_iface = nm_platform_link_get_name (master_ifindex); + const char *slave_type = NULL; + gboolean success = FALSE; + + switch (nm_platform_link_get_type (master_ifindex)) { + case NM_LINK_TYPE_BRIDGE: + slave_type = NM_SETTING_BRIDGE_SETTING_NAME; + success = nm_bridge_update_slave_connection (device, connection); + break; + case NM_LINK_TYPE_BOND: + slave_type = NM_SETTING_BOND_SETTING_NAME; + success = TRUE; + break; + case NM_LINK_TYPE_TEAM: + slave_type = NM_SETTING_TEAM_SETTING_NAME; + success = nm_team_update_slave_connection (device, connection); + break; + default: + g_warn_if_reached (); + break; + } + + if (!success) + nm_log_err (LOGD_DEVICE, "(%s): failed to read slave configuration", ifname); + + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, master_iface, + NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type, + NULL); + } + + s_ip4 = nm_setting_ip4_config_new (); nm_connection_add_setting (connection, s_ip4); + if (priv->ip4_config) + nm_ip4_config_update_setting (priv->ip4_config, (NMSettingIP4Config *) s_ip4); + else + g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NULL); + + s_ip6 = nm_setting_ip6_config_new (); nm_connection_add_setting (connection, s_ip6); + if (priv->ip6_config) + nm_ip6_config_update_setting (priv->ip6_config, (NMSettingIP6Config *) s_ip6); + else + g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); klass->update_connection (device, connection); /* Check the connection in case of update_connection() bug. */ g_return_val_if_fail (nm_connection_verify (connection, NULL), NULL); + /* Ignore the connection if it has no IP configuration, + * no slave configuration, and is not a master interface. + */ + ip4_method = nm_setting_ip4_config_get_method (NM_SETTING_IP4_CONFIG (s_ip4)); + ip6_method = nm_setting_ip6_config_get_method (NM_SETTING_IP6_CONFIG (s_ip6)); + if ( strcmp (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0 + && strcmp (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0 + && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con)) + && !nm_platform_link_supports_slaves (priv->ifindex)) { + g_object_unref (connection); + connection = NULL; + } + return connection; } @@ -1806,7 +1876,6 @@ nm_device_check_connection_compatible (NMDevice *device, * @device: #NMDevice instance * * This is a convenience function to determine whether connection assumption - * via old match_l2_config() or new update_connection() virtual functions * is available for this device. * * Use this function when you need to determine whether full cleanup should @@ -1821,9 +1890,7 @@ nm_device_check_connection_compatible (NMDevice *device, gboolean nm_device_can_assume_connections (NMDevice *device) { - NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device); - - return klass->match_l2_config || klass->update_connection; + return !!NM_DEVICE_GET_CLASS (device)->update_connection; } static void @@ -1935,7 +2002,9 @@ master_ready_cb (NMActiveConnection *active, master = nm_active_connection_get_master (active); priv->master = g_object_ref (nm_active_connection_get_device (master)); - nm_device_master_add_slave (priv->master, self); + nm_device_master_add_slave (priv->master, + self, + nm_active_connection_get_assumed (active) ? FALSE : TRUE); nm_log_dbg (LOGD_DEVICE, "(%s): master connection ready; master device %s", nm_device_get_iface (self), @@ -1952,29 +2021,7 @@ master_ready_cb (NMActiveConnection *active, static NMActStageReturn act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; - NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request); - - if (nm_active_connection_get_master (active)) { - /* If the master connection is ready for slaves, attach ourselves */ - if (nm_active_connection_get_master_ready (active)) - master_ready_cb (active, NULL, self); - else { - nm_log_dbg (LOGD_DEVICE, "(%s): waiting for master connection to become ready", - nm_device_get_iface (self)); - - /* Attach a signal handler and wait for the master connection to begin activating */ - g_assert (priv->master_ready_id == 0); - priv->master_ready_id = g_signal_connect (active, - "notify::" NM_ACTIVE_CONNECTION_INT_MASTER_READY, - (GCallback) master_ready_cb, - self); - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } - } - - return ret; + return NM_ACT_STAGE_RETURN_SUCCESS; } /* @@ -1989,8 +2036,9 @@ nm_device_activate_stage1_device_prepare (gpointer user_data) NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const char *iface; - NMActStageReturn ret; + NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request); /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, 0); @@ -2001,16 +2049,36 @@ nm_device_activate_stage1_device_prepare (gpointer user_data) nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface); nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); - ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason); - if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { - goto out; - } else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - goto out; + /* Assumed connections were already set up outside NetworkManager */ + if (!nm_active_connection_get_assumed (active)) { + ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason); + if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { + goto out; + } else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + goto out; + } + g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); } - g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); - nm_device_activate_schedule_stage2_device_config (self); + if (nm_active_connection_get_master (active)) { + /* If the master connection is ready for slaves, attach ourselves */ + if (nm_active_connection_get_master_ready (active)) + master_ready_cb (active, NULL, self); + else { + nm_log_dbg (LOGD_DEVICE, "(%s): waiting for master connection to become ready", + nm_device_get_iface (self)); + + /* Attach a signal handler and wait for the master connection to begin activating */ + g_assert (priv->master_ready_id == 0); + priv->master_ready_id = g_signal_connect (active, + "notify::" NM_ACTIVE_CONNECTION_INT_MASTER_READY, + (GCallback) master_ready_cb, + self); + /* Postpone */ + } + } else + nm_device_activate_schedule_stage2_device_config (self); out: nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) complete.", iface); @@ -2043,17 +2111,6 @@ nm_device_activate_schedule_stage1_device_prepare (NMDevice *self) static NMActStageReturn act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); - GSList *iter; - - /* If we have slaves that aren't yet enslaved, do that now */ - for (iter = priv->slaves; iter; iter = g_slist_next (iter)) { - SlaveInfo *info = iter->data; - - if (nm_device_get_state (info->slave) == NM_DEVICE_STATE_IP_CONFIG) - nm_device_enslave_slave (dev, info->slave, nm_device_get_connection (info->slave)); - } - /* Nothing to do */ return NM_ACT_STAGE_RETURN_SUCCESS; } @@ -2069,10 +2126,13 @@ static gboolean nm_device_activate_stage2_device_config (gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); - const char * iface; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const char *iface; NMActStageReturn ret; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; gboolean no_firmware = FALSE; + NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request); + GSList *iter; /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, 0); @@ -2081,23 +2141,33 @@ nm_device_activate_stage2_device_config (gpointer user_data) nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) starting...", iface); nm_device_state_changed (self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE); - if (!nm_device_bring_up (self, FALSE, &no_firmware)) { - if (no_firmware) - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_FIRMWARE_MISSING); - else - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED); - goto out; + /* Assumed connections were already set up outside NetworkManager */ + if (!nm_active_connection_get_assumed (active)) { + if (!nm_device_bring_up (self, FALSE, &no_firmware)) { + if (no_firmware) + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_FIRMWARE_MISSING); + else + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + goto out; + } + + ret = NM_DEVICE_GET_CLASS (self)->act_stage2_config (self, &reason); + if (ret == NM_ACT_STAGE_RETURN_POSTPONE) + goto out; + else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + goto out; + } + g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); } - ret = NM_DEVICE_GET_CLASS (self)->act_stage2_config (self, &reason); - if (ret == NM_ACT_STAGE_RETURN_POSTPONE) - goto out; - else if (ret == NM_ACT_STAGE_RETURN_FAILURE) - { - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - goto out; + /* If we have slaves that aren't yet enslaved, do that now */ + for (iter = priv->slaves; iter; iter = g_slist_next (iter)) { + SlaveInfo *info = iter->data; + + if (nm_device_get_state (info->slave) == NM_DEVICE_STATE_IP_CONFIG) + nm_device_enslave_slave (self, info->slave, nm_device_get_connection (info->slave)); } - g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) successful.", iface); @@ -3602,7 +3672,6 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data) NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const char *iface; - int ifindex; NMActiveConnection *master; NMDevice *master_device; @@ -3615,10 +3684,11 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data) nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) started...", iface); nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); - /* Make sure the interface is up before trying to do anything with it */ - ifindex = nm_device_get_ip_ifindex (self); - if (ifindex && !nm_platform_link_is_up (ifindex)) - nm_platform_link_set_up (ifindex); + /* Device should be up before we can do anything with it */ + if (!nm_platform_link_is_up (nm_device_get_ip_ifindex (self))) { + nm_log_warn (LOGD_DEVICE, "(%s): interface %s not up for IP configuration", + iface, nm_device_get_ip_iface (self)); + } /* If the device is a slave, then we don't do any IP configuration but we * use the IP config stage to indicate to the master we're ready for @@ -4009,7 +4079,6 @@ nm_device_activate_ip4_config_commit (gpointer user_data) const char *iface, *method; NMConnection *connection; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - int ifindex; /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, AF_INET); @@ -4023,10 +4092,11 @@ nm_device_activate_ip4_config_commit (gpointer user_data) connection = nm_act_request_get_connection (req); g_assert (connection); - /* Make sure the interface is up again just because */ - ifindex = nm_device_get_ip_ifindex (self); - if (ifindex && !nm_platform_link_is_up (ifindex)) - nm_platform_link_set_up (ifindex); + /* Device should be up before we can do anything with it */ + if (!nm_platform_link_is_up (nm_device_get_ip_ifindex (self))) { + nm_log_warn (LOGD_DEVICE, "(%s): interface %s not up for IP configuration", + iface, nm_device_get_ip_iface (self)); + } /* NULL to use the existing priv->dev_ip4_config */ if (!ip4_config_merge_and_apply (self, NULL, TRUE, &reason)) { @@ -4102,7 +4172,6 @@ nm_device_activate_ip6_config_commit (gpointer user_data) const char *iface; NMConnection *connection; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - int ifindex; /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, AF_INET6); @@ -4116,10 +4185,8 @@ nm_device_activate_ip6_config_commit (gpointer user_data) connection = nm_act_request_get_connection (req); g_assert (connection); - /* Make sure the interface is up again just because */ - ifindex = nm_device_get_ip_ifindex (self); - if (ifindex && !nm_platform_link_is_up (ifindex)) - nm_platform_link_set_up (ifindex); + /* Device should be up before we can do anything with it */ + g_warn_if_fail (nm_platform_link_is_up (nm_device_get_ip_ifindex (self))); /* Allow setting MTU etc */ if (NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit) @@ -4571,28 +4638,17 @@ nm_device_activate (NMDevice *self, NMActRequest *req) NM_DEVICE_STATE_REASON_NOW_MANAGED); } - g_assert (nm_device_connection_is_available (self, connection)); - priv->act_request = g_object_ref (req); g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION); - if (priv->state_reason == NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) { - /* If it's an assumed connection, let the device subclass short-circuit - * the normal connection process and just copy its IP configs from the - * interface. - */ - nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); - nm_device_activate_schedule_stage3_ip_config_start (self); - } else { - /* HACK: update the state a bit early to avoid a race between the - * scheduled stage1 handler and nm_policy_device_change_check() thinking - * that the activation request isn't deferred because the deferred bit - * gets cleared a bit too early, when the connection becomes valid. - */ - nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); + /* HACK: update the state a bit early to avoid a race between the + * scheduled stage1 handler and nm_policy_device_change_check() thinking + * that the activation request isn't deferred because the deferred bit + * gets cleared a bit too early, when the connection becomes valid. + */ + nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); - nm_device_activate_schedule_stage1_device_prepare (self); - } + nm_device_activate_schedule_stage1_device_prepare (self); } /* @@ -6339,8 +6395,107 @@ nm_device_get_state (NMDevice *device) return NM_DEVICE_GET_PRIVATE (device)->state; } +static NMIP4Config * +find_ip4_lease_config (NMDevice *device, + NMConnection *connection, + NMIP4Config *ext_ip4_config) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + const char *ip_iface = nm_device_get_ip_iface (device); + GSList *leases, *liter; + NMIP4Config *found = NULL; + + g_return_val_if_fail (NM_IS_IP4_CONFIG (ext_ip4_config), NULL); + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + leases = nm_dhcp_manager_get_lease_ip_configs (priv->dhcp_manager, + ip_iface, + nm_connection_get_uuid (connection), + FALSE); + for (liter = leases; liter && !found; liter = liter->next) { + NMIP4Config *lease_config = liter->data; + const NMPlatformIP4Address *address = nm_ip4_config_get_address (lease_config, 0); + guint32 gateway = nm_ip4_config_get_gateway (lease_config); + + g_assert (address); + if (!nm_ip4_config_address_exists (ext_ip4_config, address)) + continue; + if (gateway != nm_ip4_config_get_gateway (ext_ip4_config)) + continue; + found = g_object_ref (lease_config); + } + + g_slist_free_full (leases, g_object_unref); + return found; +} + static void -update_ip_config (NMDevice *self) +capture_lease_config (NMDevice *device, + NMIP4Config *ext_ip4_config, + NMIP4Config **out_ip4_config, + NMIP6Config *ext_ip6_config, + NMIP6Config **out_ip6_config) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + const GSList *connections, *citer; + guint i; + gboolean dhcp_used = FALSE; + + /* Ensure at least one address on the device has a non-infinite lifetime, + * otherwise DHCP cannot possibly be active on the device right now. + */ + if (ext_ip4_config && out_ip4_config) { + for (i = 0; i < nm_ip4_config_get_num_addresses (ext_ip4_config); i++) { + const NMPlatformIP4Address *addr = nm_ip4_config_get_address (ext_ip4_config, i); + + if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { + dhcp_used = TRUE; + break; + } + } + } else if (ext_ip6_config && out_ip6_config) { + for (i = 0; i < nm_ip6_config_get_num_addresses (ext_ip6_config); i++) { + const NMPlatformIP6Address *addr = nm_ip6_config_get_address (ext_ip6_config, i); + + if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { + dhcp_used = TRUE; + break; + } + } + } else { + g_return_if_fail ( (ext_ip6_config && out_ip6_config) + || (ext_ip4_config && out_ip4_config)); + } + + if (!dhcp_used) + return; + + connections = nm_connection_provider_get_connections (priv->con_provider); + for (citer = connections; citer; citer = citer->next) { + NMConnection *candidate = citer->data; + const char *method; + + if (!nm_device_check_connection_compatible (device, candidate, NULL)) + continue; + + /* IPv4 leases */ + method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP4_CONFIG); + if (out_ip4_config && strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) { + *out_ip4_config = find_ip4_lease_config (device, candidate, ext_ip4_config); + if (*out_ip4_config) + return; + } + + /* IPv6 leases */ + method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP6_CONFIG); + if (out_ip6_config && strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) { + /* FIXME: implement find_ip6_lease_config() */ + } + } +} + +static void +update_ip_config (NMDevice *self, gboolean capture_dhcp) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int ifindex; @@ -6353,7 +6508,12 @@ update_ip_config (NMDevice *self) /* IPv4 */ g_clear_object (&priv->ext_ip4_config); priv->ext_ip4_config = nm_ip4_config_capture (ifindex); + if (priv->ext_ip4_config) { + if (capture_dhcp) { + g_clear_object (&priv->dev_ip4_config); + capture_lease_config (self, priv->ext_ip4_config, &priv->dev_ip4_config, NULL, NULL); + } if (priv->dev_ip4_config) nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config); if (priv->vpn4_config) @@ -6389,6 +6549,12 @@ update_ip_config (NMDevice *self) } } +void +nm_device_capture_initial_config (NMDevice *dev) +{ + update_ip_config (dev, TRUE); +} + static gboolean queued_ip_config_change (gpointer user_data) { @@ -6400,7 +6566,7 @@ queued_ip_config_change (gpointer user_data) return TRUE; priv->queued_ip_config_id = 0; - update_ip_config (self); + update_ip_config (self, FALSE); return FALSE; } @@ -6562,125 +6728,6 @@ spec_match_list (NMDevice *device, const GSList *specs) return matched; } -static gboolean -ip4_match_config (NMDevice *self, NMConnection *connection) -{ - NMSettingIP4Config *s_ip4; - int i, num; - GSList *leases, *iter; - NMDHCPManager *dhcp_mgr; - const char *method; - - /* Get any saved leases that apply to this connection */ - dhcp_mgr = nm_dhcp_manager_get (); - leases = nm_dhcp_manager_get_lease_config (dhcp_mgr, - nm_device_get_iface (self), - nm_connection_get_uuid (connection), - FALSE); - g_object_unref (dhcp_mgr); - - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { - gboolean found = FALSE; - - /* Find at least one lease's address on the device */ - for (iter = leases; iter; iter = g_slist_next (iter)) { - NMIP4Config *ip4_config = iter->data; - const NMPlatformIP4Address *address = nm_ip4_config_get_address (ip4_config, 0); - - if (address && nm_platform_ip4_address_exists (nm_device_get_ip_ifindex (self), - address->address, - address->plen)) { - found = TRUE; /* Yay, device has same address as a lease */ - break; - } - } - g_slist_free_full (leases, g_object_unref); - return found; - } else { - /* Maybe the connection used to be DHCP and there are stale leases; ignore them */ - g_slist_free_full (leases, g_object_unref); - } - - if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) { - // FIXME: Enforce no ipv4 addresses? - return TRUE; - } - - /* 'shared' and 'link-local' aren't supported methods because 'shared' - * requires too much iptables and dnsmasq state to be reclaimed, and - * avahi-autoipd isn't smart enough to allow the link-local address to be - * determined at any point other than when it was first assigned. - */ - if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) - return FALSE; - - /* Everything below for static addressing */ - - /* Find all IP4 addresses of this connection on the device */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (s_ip4) { - num = nm_setting_ip4_config_get_num_addresses (s_ip4); - for (i = 0; i < num; i++) { - NMIP4Address *addr = nm_setting_ip4_config_get_address (s_ip4, i); - - if (!nm_platform_ip4_address_exists (nm_device_get_ip_ifindex (self), - nm_ip4_address_get_address (addr), - nm_ip4_address_get_prefix (addr))) - return FALSE; - } - } - - /* Success; all the connection's static IP addresses are assigned to the device */ - return TRUE; -} - -/** - * nm_device_find_assumable_connection: - * @device: an #NMDevice - * @connections: (element-type NMConnection): a list of connections - * - * Searches @connections for one that matches the currently-configured - * state of @device (in both L2 and L3 configuration). That is, it - * looks for the connection such that if you activated that connection - * on @device, it would result in @device having the configuration - * that it has now. This is used at startup to attempt to match - * already-active devices with corresponding #NMConnections. - * - * Some device types (eg, Wi-Fi) and subtypes (eg, PPPoE) can't be - * matched reliably, so this will always fail for those devices. - * - * Returns: (transfer none): an #NMConnection that matches @device's - * current state, or %NULL if none match. - */ -NMConnection * -nm_device_find_assumable_connection (NMDevice *device, const GSList *connections) -{ - const GSList *iter; - - g_return_val_if_fail (NM_IS_DEVICE (device), NULL); - - if (!NM_DEVICE_GET_CLASS (device)->match_l2_config) - return NULL; - - for (iter = connections; iter; iter = iter->next) { - NMConnection *candidate = NM_CONNECTION (iter->data); - - if (!nm_device_check_connection_compatible (device, candidate, NULL)) - continue; - - if (!ip4_match_config (device, candidate)) - continue; - - /* FIXME: match IPv6 config */ - - if (NM_DEVICE_GET_CLASS (device)->match_l2_config (device, candidate)) - return candidate; - } - - return NULL; -} - void nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout) { diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 63b0699572..69d81754ce 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -180,28 +180,13 @@ typedef struct { gboolean (* spec_match_list) (NMDevice *self, const GSList *specs); - /* FIXME: We currently support match_l2_config() virtual function for - * compatibility. When match_l2_config() is not present, we use the - * new update_connection() virtual function which should first call - * NMDevice's implementation and then perform type-specific adjustments. - * - * Therefore subclasses that implement the new API *must* leave - * match_l2_config set to NULL and implement update_connection, while - * subclasses that implement the old API *must* set match_l2_config - * (update_connection is ignored). - * - * Subclasses which don't implement any of the APIs for connection assumption - * *should* leave generate_connection NULL. - * - * The update_connection() virtual function is also used for live - * reconfiguration of the connection according to link level changes. - */ - gboolean (* match_l2_config) (NMDevice *self, NMConnection *connection); + /* Update the connection with currently configured L2 settings */ void (* update_connection) (NMDevice *device, NMConnection *connection); gboolean (* enslave_slave) (NMDevice *self, NMDevice *slave, - NMConnection *connection); + NMConnection *connection, + gboolean configure); gboolean (* release_slave) (NMDevice *self, NMDevice *slave); @@ -246,8 +231,10 @@ void nm_device_set_vpn4_config (NMDevice *dev, NMIP4Config *config) NMIP6Config * nm_device_get_ip6_config (NMDevice *dev); void nm_device_set_vpn6_config (NMDevice *dev, NMIP6Config *config); +void nm_device_capture_initial_config (NMDevice *dev); + /* Master */ -gboolean nm_device_master_add_slave (NMDevice *dev, NMDevice *slave); +gboolean nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure); GSList * nm_device_master_get_slaves (NMDevice *dev); gboolean nm_device_is_master (NMDevice *dev); diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.c b/src/dhcp-manager/nm-dhcp-dhclient-utils.c index 07c049e7ea..64e8f72669 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient-utils.c +++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.c @@ -25,6 +25,8 @@ #include <ctype.h> #include "nm-dhcp-dhclient-utils.h" +#include "nm-ip4-config.h" +#include "nm-utils.h" #define CLIENTID_TAG "send dhcp-client-identifier" #define CLIENTID_FORMAT CLIENTID_TAG " \"%s\"; # added by NetworkManager" @@ -424,3 +426,234 @@ nm_dhcp_dhclient_save_duid (const char *leasefile, return success; } +static void +add_lease_option (GHashTable *hash, char *line) +{ + char *spc; + size_t len; + + /* Find the space after "option" */ + spc = strchr (line, ' '); + if (!spc) + return; + + /* Find the option tag's data, which is after the second space */ + if (g_str_has_prefix (line, "option ")) { + while (g_ascii_isspace (*spc)) + spc++; + spc = strchr (spc + 1, ' '); + if (!spc) + return; + } + + /* Split the line at the space */ + *spc = '\0'; + spc++; + + /* Kill the ';' at the end of the line, if any */ + len = strlen (spc); + if (*(spc + len - 1) == ';') + *(spc + len - 1) = '\0'; + + /* Strip leading quote */ + while (g_ascii_isspace (*spc)) + spc++; + if (*spc == '"') + spc++; + + /* Strip trailing quote */ + len = strlen (spc); + if (len > 0 && spc[len - 1] == '"') + spc[len - 1] = '\0'; + + if (spc[0]) + g_hash_table_insert (hash, g_strdup (line), g_strdup (spc)); +} + +#define LEASE_INVALID G_MININT64 +static GTimeSpan +lease_validity_span (const char *str_expire, GDateTime *now) +{ + GDateTime *expire = NULL; + struct tm expire_tm; + GTimeSpan span; + + g_return_val_if_fail (now != NULL, LEASE_INVALID); + g_return_val_if_fail (str_expire != NULL, LEASE_INVALID); + + /* Skip initial number (day of week?) */ + if (!isdigit (*str_expire++)) + return LEASE_INVALID; + if (!isspace (*str_expire++)) + return LEASE_INVALID; + /* Read lease expiration (in UTC) */ + if (!strptime (str_expire, "%t%Y/%m/%d %H:%M:%S", &expire_tm)) + return LEASE_INVALID; + + expire = g_date_time_new_utc (expire_tm.tm_year + 1900, + expire_tm.tm_mon + 1, + expire_tm.tm_mday, + expire_tm.tm_hour, + expire_tm.tm_min, + expire_tm.tm_sec); + if (!expire) + return LEASE_INVALID; + + span = g_date_time_difference (expire, now); + g_date_time_unref (expire); + + /* GDateTime only supports a range of less then 10000 years, so span can + * not overflow or be equal to LEASE_INVALID */ + return span; +} + +/** + * nm_dhcp_dhclient_read_lease_ip_configs: + * @iface: the interface name to match leases with + * @contents: the contents of a dhclient leasefile + * @ipv6: whether to read IPv4 or IPv6 leases + * @now: the current UTC date/time; pass %NULL to automatically use current + * UTC time. Testcases may need a different value for 'now' + * + * Reads dhclient leases from @contents and parses them into either + * #NMIP4Config or #NMIP6Config objects depending on the value of @ipv6. + * + * Returns: a #GSList of #NMIP4Config objects (if @ipv6 is %FALSE) or a list of + * #NMIP6Config objects (if @ipv6 is %TRUE) containing the lease data. + */ +GSList * +nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, + const char *contents, + gboolean ipv6, + GDateTime *now) +{ + GSList *parsed = NULL, *iter, *leases = NULL; + char **line, **split = NULL; + GHashTable *hash = NULL; + + g_return_val_if_fail (contents != NULL, NULL); + + split = g_strsplit_set (contents, "\n\r", -1); + if (!split) + return NULL; + + for (line = split; line && *line; line++) { + *line = g_strstrip (*line); + + if (*line[0] == '#') { + /* Comment */ + } else if (!strcmp (*line, "}")) { + /* Lease ends */ + parsed = g_slist_append (parsed, hash); + hash = NULL; + } else if (!strcmp (*line, "lease {")) { + /* Beginning of a new lease */ + if (hash) { + /* Ignore malformed lease that doesn't end before new one starts */ + g_hash_table_destroy (hash); + } + + hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + } else if (hash && strlen (*line)) + add_lease_option (hash, *line); + } + g_strfreev (split); + + /* Check if the last lease in the file was properly ended */ + if (hash) { + /* Ignore malformed lease that doesn't end before new one starts */ + g_hash_table_destroy (hash); + hash = NULL; + } + + if (now) + g_date_time_ref (now); + else + now = g_date_time_new_now_utc (); + + for (iter = parsed; iter; iter = g_slist_next (iter)) { + NMIP4Config *ip4; + NMPlatformIP4Address address; + const char *value; + GTimeSpan expiry; + guint32 tmp, gw = 0; + + hash = iter->data; + + /* Make sure this lease is for the interface we want */ + value = g_hash_table_lookup (hash, "interface"); + if (!value || strcmp (value, iface)) + continue; + + value = g_hash_table_lookup (hash, "expire"); + if (!value) + continue; + expiry = lease_validity_span (value, now); + if (expiry == LEASE_INVALID) + continue; + + /* scale expiry to seconds (and CLAMP into the range of guint32) */ + expiry = CLAMP (expiry / G_TIME_SPAN_SECOND, 0, G_MAXUINT32-1); + if (expiry <= 0) { + /* the address is already expired. Don't even add it. */ + continue; + } + + memset (&address, 0, sizeof (address)); + + /* IP4 address */ + value = g_hash_table_lookup (hash, "fixed-address"); + if (!value) + continue; + if (!inet_pton (AF_INET, value, &address.address)) + continue; + + /* Gateway */ + value = g_hash_table_lookup (hash, "option routers"); + if (!value) + continue; + if (!inet_pton (AF_INET, value, &gw)) + continue; + + /* Netmask */ + value = g_hash_table_lookup (hash, "option subnet-mask"); + if (value && inet_pton (AF_INET, value, &tmp)) + address.plen = nm_utils_ip4_netmask_to_prefix (tmp); + + /* Get default netmask for the IP according to appropriate class. */ + if (!address.plen) + address.plen = nm_utils_ip4_get_default_prefix (address.address); + + address.lifetime = address.preferred = expiry; + + ip4 = nm_ip4_config_new (); + nm_ip4_config_add_address (ip4, &address); + nm_ip4_config_set_gateway (ip4, gw); + + value = g_hash_table_lookup (hash, "option domain-name-servers"); + if (value) { + char **dns, **dns_iter; + + dns = g_strsplit_set (value, ",", -1); + for (dns_iter = dns; dns_iter && *dns_iter; dns_iter++) { + if (inet_pton (AF_INET, *dns_iter, &tmp)) + nm_ip4_config_add_nameserver (ip4, tmp); + } + if (dns) + g_strfreev (dns); + } + + value = g_hash_table_lookup (hash, "option domain-name"); + if (value && value[0]) + nm_ip4_config_add_domain (ip4, value); + + /* FIXME: static routes */ + + leases = g_slist_append (leases, ip4); + } + + g_date_time_unref (now); + g_slist_free_full (parsed, (GDestroyNotify) g_hash_table_destroy); + return leases; +} + diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.h b/src/dhcp-manager/nm-dhcp-dhclient-utils.h index 95cb056807..d2caaa475b 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient-utils.h +++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.h @@ -44,5 +44,10 @@ gboolean nm_dhcp_dhclient_save_duid (const char *leasefile, const char *escaped_duid, GError **error); +GSList *nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, + const char *contents, + gboolean ipv6, + GDateTime *now); + #endif /* NM_DHCP_DHCLIENT_UTILS_H */ diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index e6fa77a675..5f9acf39f9 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -32,6 +32,7 @@ #include <stdio.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <ctype.h> #include <config.h> @@ -135,215 +136,31 @@ get_dhclient_leasefile (const char *iface, return NULL; } -static void -add_lease_option (GHashTable *hash, char *line) -{ - char *spc; - - spc = strchr (line, ' '); - if (!spc) { - nm_log_warn (LOGD_DHCP, "DHCP lease file line '%s' did not contain a space", line); - return; - } - - /* If it's an 'option' line, split at second space */ - if (g_str_has_prefix (line, "option ")) { - spc = strchr (spc + 1, ' '); - if (!spc) { - nm_log_warn (LOGD_DHCP, "DHCP lease file option line '%s' did not contain a second space", - line); - return; - } - } - - /* Split the line at the space */ - *spc = '\0'; - spc++; - - /* Kill the ';' at the end of the line, if any */ - if (*(spc + strlen (spc) - 1) == ';') - *(spc + strlen (spc) - 1) = '\0'; - - /* Treat 'interface' specially */ - if (g_str_has_prefix (line, "interface")) { - if (*(spc) == '"') - spc++; /* Jump past the " */ - if (*(spc + strlen (spc) - 1) == '"') - *(spc + strlen (spc) - 1) = '\0'; /* Kill trailing " */ - } - - g_hash_table_insert (hash, g_strdup (line), g_strdup (spc)); -} - GSList * -nm_dhcp_dhclient_get_lease_config (const char *iface, const char *uuid, gboolean ipv6) +nm_dhcp_dhclient_get_lease_ip_configs (const char *iface, + const char *uuid, + gboolean ipv6) { - GSList *parsed = NULL, *iter, *leases = NULL; char *contents = NULL; char *leasefile; - char **line, **split = NULL; - GHashTable *hash = NULL; - - /* IPv6 not supported */ - if (ipv6) - return NULL; + GSList *leases = NULL; leasefile = get_dhclient_leasefile (iface, uuid, FALSE, NULL); if (!leasefile) return NULL; - if (!g_file_test (leasefile, G_FILE_TEST_EXISTS)) - goto out; + if ( g_file_test (leasefile, G_FILE_TEST_EXISTS) + && g_file_get_contents (leasefile, &contents, NULL, NULL) + && contents + && contents[0]) + leases = nm_dhcp_dhclient_read_lease_ip_configs (iface, contents, ipv6, NULL); - if (!g_file_get_contents (leasefile, &contents, NULL, NULL)) - goto out; - - split = g_strsplit_set (contents, "\n\r", -1); + g_free (leasefile); g_free (contents); - if (!split) - goto out; - - for (line = split; line && *line; line++) { - *line = g_strstrip (*line); - - if (!strcmp (*line, "}")) { - /* Lease ends */ - parsed = g_slist_append (parsed, hash); - hash = NULL; - } else if (!strcmp (*line, "lease {")) { - /* Beginning of a new lease */ - if (hash) { - nm_log_warn (LOGD_DHCP, "DHCP lease file %s malformed; new lease started " - "without ending previous lease", - leasefile); - g_hash_table_destroy (hash); - } - - hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - } else if (strlen (*line)) - add_lease_option (hash, *line); - } - g_strfreev (split); - - /* Check if the last lease in the file was properly ended */ - if (hash) { - nm_log_warn (LOGD_DHCP, "DHCP lease file %s malformed; new lease started " - "without ending previous lease", - leasefile); - g_hash_table_destroy (hash); - hash = NULL; - } - - for (iter = parsed; iter; iter = g_slist_next (iter)) { - NMIP4Config *ip4; - NMPlatformIP4Address address; - const char *data; - guint32 tmp; - guint32 plen; - struct tm expire; - - hash = iter->data; - - /* Make sure this lease is for the interface we want */ - data = g_hash_table_lookup (hash, "interface"); - if (!data || strcmp (data, iface)) - continue; - - data = g_hash_table_lookup (hash, "expire"); - if (data) { - time_t now_tt; - struct tm *now; - - /* Read lease expiration (in UTC) */ - if (!strptime (data, "%w %Y/%m/%d %H:%M:%S", &expire)) { - nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file expire time '%s'", - data); - continue; - } - - now_tt = time (NULL); - now = gmtime(&now_tt); - - /* Ignore this lease if it's already expired */ - if (expire.tm_year < now->tm_year) - continue; - else if (expire.tm_year == now->tm_year) { - if (expire.tm_mon < now->tm_mon) - continue; - else if (expire.tm_mon == now->tm_mon) { - if (expire.tm_mday < now->tm_mday) - continue; - else if (expire.tm_mday == now->tm_mday) { - if (expire.tm_hour < now->tm_hour) - continue; - else if (expire.tm_hour == now->tm_hour) { - if (expire.tm_min < now->tm_min) - continue; - else if (expire.tm_min == now->tm_min) { - if (expire.tm_sec <= now->tm_sec) - continue; - } - } - } - } - } - /* If we get this far, the lease hasn't expired */ - } - - data = g_hash_table_lookup (hash, "fixed-address"); - if (!data) - continue; - ip4 = nm_ip4_config_new (); - memset (&address, 0, sizeof (address)); - - /* IP4 address */ - if (!inet_pton (AF_INET, data, &tmp)) { - nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 address '%s'", data); - goto error; - } - address.address = tmp; - - /* Netmask */ - data = g_hash_table_lookup (hash, "option subnet-mask"); - if (data) { - if (!inet_pton (AF_INET, data, &tmp)) { - nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 subnet mask '%s'", data); - goto error; - } - plen = nm_utils_ip4_netmask_to_prefix (tmp); - } else { - /* Get default netmask for the IP according to appropriate class. */ - plen = nm_utils_ip4_get_default_prefix (address.address); - } - address.plen = plen; - - /* Gateway */ - data = g_hash_table_lookup (hash, "option routers"); - if (data) { - if (!inet_pton (AF_INET, data, &tmp)) { - nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 gateway '%s'", data); - goto error; - } - nm_ip4_config_set_gateway (ip4, tmp); - } - - nm_ip4_config_add_address (ip4, &address); - leases = g_slist_append (leases, ip4); - continue; - - error: - g_object_unref (ip4); - } - -out: - g_slist_free_full (parsed, (GDestroyNotify) g_hash_table_destroy); - g_free (leasefile); return leases; } - - static gboolean merge_dhclient_config (const char *iface, const char *conf_file, diff --git a/src/dhcp-manager/nm-dhcp-dhclient.h b/src/dhcp-manager/nm-dhcp-dhclient.h index 89039189d8..20219a63e1 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.h +++ b/src/dhcp-manager/nm-dhcp-dhclient.h @@ -41,7 +41,9 @@ typedef struct { GType nm_dhcp_dhclient_get_type (void); -GSList *nm_dhcp_dhclient_get_lease_config (const char *iface, const char *uuid, gboolean ipv6); +GSList *nm_dhcp_dhclient_get_lease_ip_configs (const char *iface, + const char *uuid, + gboolean ipv6); const char *nm_dhcp_dhclient_get_path (const char *try_first); diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c index 7709720428..2a1e0681cc 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.c +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -71,12 +71,6 @@ nm_dhcp_dhcpcd_get_path (const char *try_first) return *path; } -GSList * -nm_dhcp_dhcpcd_get_lease_config (const char *iface, const char *uuid, gboolean ipv6) -{ - return NULL; -} - static void dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED) { diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.h b/src/dhcp-manager/nm-dhcp-dhcpcd.h index c90dcb74d8..fb6b0628f4 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.h +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.h @@ -41,8 +41,6 @@ typedef struct { GType nm_dhcp_dhcpcd_get_type (void); -GSList *nm_dhcp_dhcpcd_get_lease_config (const char *iface, const char *uuid, gboolean ipv6); - const char *nm_dhcp_dhcpcd_get_path (const char *try_first); #endif /* NM_DHCP_DHCPCD_H */ diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index b7359d70bc..89d651864e 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -71,7 +71,7 @@ typedef GSList * (*GetLeaseConfigFunc) (const char *iface, const char *uuid, gbo typedef struct { GType client_type; - GetLeaseConfigFunc get_lease_config_func; + GetLeaseConfigFunc get_lease_ip_configs_func; NMDBusManager * dbus_mgr; guint new_conn_id; @@ -313,7 +313,7 @@ get_client_type (const char *client, GError **error) g_set_error_literal (error, NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT, _("no usable DHCP client could be found.")); - return 0; + return G_TYPE_INVALID; } } @@ -322,7 +322,7 @@ get_client_type (const char *client, GError **error) g_set_error_literal (error, NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT, _("'dhclient' could be found.")); - return 0; + return G_TYPE_INVALID; } return NM_TYPE_DHCP_DHCLIENT; } @@ -332,7 +332,7 @@ get_client_type (const char *client, GError **error) g_set_error_literal (error, NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT, _("'dhcpcd' could be found.")); - return 0; + return G_TYPE_INVALID; } return NM_TYPE_DHCP_DHCPCD; } @@ -340,7 +340,7 @@ get_client_type (const char *client, GError **error) g_set_error (error, NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT, _("unsupported DHCP client '%s'"), client); - return 0; + return G_TYPE_INVALID; } NMDHCPManager * @@ -362,15 +362,14 @@ nm_dhcp_manager_get (void) /* Client-specific setup */ client = nm_config_get_dhcp_client (nm_config_get ()); priv->client_type = get_client_type (client, &error); + if (priv->client_type == NM_TYPE_DHCP_DHCLIENT) - priv->get_lease_config_func = nm_dhcp_dhclient_get_lease_config; - else if (priv->client_type == NM_TYPE_DHCP_DHCPCD) - priv->get_lease_config_func = nm_dhcp_dhcpcd_get_lease_config; - else { + priv->get_lease_ip_configs_func = nm_dhcp_dhclient_get_lease_ip_configs; + else if (priv->client_type == G_TYPE_INVALID) { nm_log_warn (LOGD_DHCP, "No usable DHCP client found (%s)! DHCP configurations will fail.", error->message); - g_error_free (error); } + g_clear_error (&error); priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, @@ -610,10 +609,10 @@ nm_dhcp_manager_set_hostname_provider (NMDHCPManager *manager, } GSList * -nm_dhcp_manager_get_lease_config (NMDHCPManager *self, - const char *iface, - const char *uuid, - gboolean ipv6) +nm_dhcp_manager_get_lease_ip_configs (NMDHCPManager *self, + const char *iface, + const char *uuid, + gboolean ipv6) { NMDHCPManagerPrivate *priv; @@ -623,10 +622,8 @@ nm_dhcp_manager_get_lease_config (NMDHCPManager *self, priv = NM_DHCP_MANAGER_GET_PRIVATE (self); - if (priv->get_lease_config_func) - return priv->get_lease_config_func (iface, uuid, ipv6); - - nm_log_warn (LOGD_DHCP, "Cannot get a DHCP lease config (no usable DHCP client was found!)"); + if (priv->get_lease_ip_configs_func) + return priv->get_lease_ip_configs_func (iface, uuid, ipv6); return NULL; } diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h index 9b19f4a971..8a33df8a50 100644 --- a/src/dhcp-manager/nm-dhcp-manager.h +++ b/src/dhcp-manager/nm-dhcp-manager.h @@ -82,10 +82,10 @@ NMDHCPClient * nm_dhcp_manager_start_ip6 (NMDHCPManager *manager, guint8 *dhcp_anycast_addr, gboolean info_only); -GSList * nm_dhcp_manager_get_lease_config (NMDHCPManager *self, - const char *iface, - const char *uuid, - gboolean ipv6); +GSList * nm_dhcp_manager_get_lease_ip_configs (NMDHCPManager *self, + const char *iface, + const char *uuid, + gboolean ipv6); /* For testing only */ NMIP4Config *nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client, diff --git a/src/dhcp-manager/tests/Makefile.am b/src/dhcp-manager/tests/Makefile.am index 7698d09e86..71d706f429 100644 --- a/src/dhcp-manager/tests/Makefile.am +++ b/src/dhcp-manager/tests/Makefile.am @@ -4,6 +4,8 @@ AM_CPPFLAGS = \ -I${top_srcdir}/libnm-util \ -I${top_builddir}/libnm-util \ -I$(top_srcdir)/src/dhcp-manager \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/platform \ $(GLIB_CFLAGS) \ -DTESTDIR="\"$(abs_srcdir)\"" @@ -22,5 +24,9 @@ check-local: test-dhcp-dhclient EXTRA_DIST = \ test-dhclient-duid.leases \ - test-dhclient-commented-duid.leases + test-dhclient-commented-duid.leases \ + leases/basic.leases \ + leases/malformed1.leases \ + leases/malformed2.leases \ + leases/malformed3.leases diff --git a/src/dhcp-manager/tests/leases/basic.leases b/src/dhcp-manager/tests/leases/basic.leases new file mode 100644 index 0000000000..703d92479d --- /dev/null +++ b/src/dhcp-manager/tests/leases/basic.leases @@ -0,0 +1,31 @@ +lease { + interface "wlan0"; + fixed-address 192.168.1.180; + option subnet-mask 255.255.255.0; + option routers 192.168.1.1; + option dhcp-lease-time 600; + option dhcp-message-type 5; + option domain-name-servers 192.168.1.1; + option dhcp-server-identifier 192.168.1.1; + option broadcast-address 192.168.1.255; + renew 5 2013/11/01 19:56:15; + rebind 5 2013/11/01 20:00:44; + expire 5 2013/11/01 20:01:59; +} +lease { + interface "wlan0"; + fixed-address 10.77.52.141; + option subnet-mask 255.0.0.0; + option dhcp-lease-time 1200; + option routers 10.77.52.254; + option dhcp-message-type 5; + option dhcp-server-identifier 10.77.52.254; + option domain-name-servers 8.8.8.8,8.8.4.4; + option dhcp-renewal-time 600; + option dhcp-rebinding-time 1050; + option domain-name "morriesguest.local"; + renew 5 2013/11/01 20:01:08; + rebind 5 2013/11/01 20:05:00; + expire 5 2013/11/01 20:06:15; +} + diff --git a/src/dhcp-manager/tests/leases/malformed1.leases b/src/dhcp-manager/tests/leases/malformed1.leases new file mode 100644 index 0000000000..401d982ad4 --- /dev/null +++ b/src/dhcp-manager/tests/leases/malformed1.leases @@ -0,0 +1,15 @@ +# missing fixed-address option +lease { + interface "wlan0"; + option subnet-mask 255.255.255.0; + option routers 192.168.1.1; + option dhcp-lease-time 600; + option dhcp-message-type 5; + option domain-name-servers 192.168.1.1; + option dhcp-server-identifier 192.168.1.1; + option broadcast-address 192.168.1.255; + renew 5 2013/11/01 19:56:15; + rebind 5 2013/11/01 20:00:44; + expire 5 2013/11/01 20:01:59; +} + diff --git a/src/dhcp-manager/tests/leases/malformed2.leases b/src/dhcp-manager/tests/leases/malformed2.leases new file mode 100644 index 0000000000..adf5f6decc --- /dev/null +++ b/src/dhcp-manager/tests/leases/malformed2.leases @@ -0,0 +1,15 @@ +# missing routers option +lease { + interface "wlan0"; + fixed-address 192.168.1.180; + option subnet-mask 255.255.255.0; + option dhcp-lease-time 600; + option dhcp-message-type 5; + option domain-name-servers 192.168.1.1; + option dhcp-server-identifier 192.168.1.1; + option broadcast-address 192.168.1.255; + renew 5 2013/11/01 19:56:15; + rebind 5 2013/11/01 20:00:44; + expire 5 2013/11/01 20:01:59; +} + diff --git a/src/dhcp-manager/tests/leases/malformed3.leases b/src/dhcp-manager/tests/leases/malformed3.leases new file mode 100644 index 0000000000..a2afc8b6c3 --- /dev/null +++ b/src/dhcp-manager/tests/leases/malformed3.leases @@ -0,0 +1,15 @@ +# missing expire time +lease { + interface "wlan0"; + fixed-address 192.168.1.180; + option subnet-mask 255.255.255.0; + option routers 192.168.1.1; + option dhcp-lease-time 600; + option dhcp-message-type 5; + option domain-name-servers 192.168.1.1; + option dhcp-server-identifier 192.168.1.1; + option broadcast-address 192.168.1.255; + renew 5 2013/11/01 19:56:15; + rebind 5 2013/11/01 20:00:44; +} + diff --git a/src/dhcp-manager/tests/test-dhcp-dhclient.c b/src/dhcp-manager/tests/test-dhcp-dhclient.c index ee81ec99a3..a0ede1ced9 100644 --- a/src/dhcp-manager/tests/test-dhcp-dhclient.c +++ b/src/dhcp-manager/tests/test-dhcp-dhclient.c @@ -24,6 +24,7 @@ #include "nm-dhcp-dhclient-utils.h" #include "nm-utils.h" +#include "nm-ip4-config.h" #define DEBUG 0 @@ -454,6 +455,128 @@ test_write_existing_commented_duid (void) /*******************************************/ +static void +test_read_lease_ip4_config_basic (void) +{ + GError *error = NULL; + char *contents = NULL; + gboolean success; + const char *path = TESTDIR "/leases/basic.leases"; + GSList *leases; + GDateTime *now; + NMIP4Config *config; + const NMPlatformIP4Address *addr; + guint32 expected_addr; + + success = g_file_get_contents (path, &contents, NULL, &error); + g_assert_no_error (error); + g_assert (success); + + /* Date from before the least expiration */ + now = g_date_time_new_utc (2013, 11, 1, 19, 55, 32); + leases = nm_dhcp_dhclient_read_lease_ip_configs ("wlan0", contents, FALSE, now); + g_assert_cmpint (g_slist_length (leases), ==, 2); + + /* IP4Config #1 */ + config = g_slist_nth_data (leases, 0); + g_assert (NM_IS_IP4_CONFIG (config)); + + /* Address */ + g_assert_cmpint (nm_ip4_config_get_num_addresses (config), ==, 1); + g_assert (inet_aton ("192.168.1.180", (struct in_addr *) &expected_addr)); + addr = nm_ip4_config_get_address (config, 0); + g_assert_cmpint (addr->address, ==, expected_addr); + g_assert_cmpint (addr->plen, ==, 24); + + /* Gateway */ + g_assert (inet_aton ("192.168.1.1", (struct in_addr *) &expected_addr)); + g_assert_cmpint (nm_ip4_config_get_gateway (config), ==, expected_addr); + + /* DNS */ + g_assert_cmpint (nm_ip4_config_get_num_nameservers (config), ==, 1); + g_assert (inet_aton ("192.168.1.1", (struct in_addr *) &expected_addr)); + g_assert_cmpint (nm_ip4_config_get_nameserver (config, 0), ==, expected_addr); + + g_assert_cmpint (nm_ip4_config_get_num_domains (config), ==, 0); + + /* IP4Config #2 */ + config = g_slist_nth_data (leases, 1); + g_assert (NM_IS_IP4_CONFIG (config)); + + /* Address */ + g_assert_cmpint (nm_ip4_config_get_num_addresses (config), ==, 1); + g_assert (inet_aton ("10.77.52.141", (struct in_addr *) &expected_addr)); + addr = nm_ip4_config_get_address (config, 0); + g_assert_cmpint (addr->address, ==, expected_addr); + g_assert_cmpint (addr->plen, ==, 8); + + /* Gateway */ + g_assert (inet_aton ("10.77.52.254", (struct in_addr *) &expected_addr)); + g_assert_cmpint (nm_ip4_config_get_gateway (config), ==, expected_addr); + + /* DNS */ + g_assert_cmpint (nm_ip4_config_get_num_nameservers (config), ==, 2); + g_assert (inet_aton ("8.8.8.8", (struct in_addr *) &expected_addr)); + g_assert_cmpint (nm_ip4_config_get_nameserver (config, 0), ==, expected_addr); + g_assert (inet_aton ("8.8.4.4", (struct in_addr *) &expected_addr)); + g_assert_cmpint (nm_ip4_config_get_nameserver (config, 1), ==, expected_addr); + + /* Domains */ + g_assert_cmpint (nm_ip4_config_get_num_domains (config), ==, 1); + g_assert_cmpstr (nm_ip4_config_get_domain (config, 0), ==, "morriesguest.local"); + + g_slist_free_full (leases, g_object_unref); + g_date_time_unref (now); + g_free (contents); +} + +static void +test_read_lease_ip4_config_expired (void) +{ + GError *error = NULL; + char *contents = NULL; + gboolean success; + const char *path = TESTDIR "/leases/basic.leases"; + GSList *leases; + GDateTime *now; + + success = g_file_get_contents (path, &contents, NULL, &error); + g_assert_no_error (error); + g_assert (success); + + /* Date from *after* the lease expiration */ + now = g_date_time_new_utc (2013, 12, 1, 19, 55, 32); + leases = nm_dhcp_dhclient_read_lease_ip_configs ("wlan0", contents, FALSE, now); + g_assert (leases == NULL); + + g_date_time_unref (now); + g_free (contents); +} + +static void +test_read_lease_ip4_config_expect_failure (gconstpointer user_data) +{ + GError *error = NULL; + char *contents = NULL; + gboolean success; + GSList *leases; + GDateTime *now; + + success = g_file_get_contents ((const char *) user_data, &contents, NULL, &error); + g_assert_no_error (error); + g_assert (success); + + /* Date from before the least expiration */ + now = g_date_time_new_utc (2013, 11, 1, 1, 1, 1); + leases = nm_dhcp_dhclient_read_lease_ip_configs ("wlan0", contents, FALSE, now); + g_assert (leases == NULL); + + g_date_time_unref (now); + g_free (contents); +} + +/*******************************************/ + int main (int argc, char **argv) { @@ -477,6 +600,18 @@ main (int argc, char **argv) g_test_add_func ("/dhcp/dhclient/write_existing_duid", test_write_existing_duid); g_test_add_func ("/dhcp/dhclient/write_existing_commented_duid", test_write_existing_commented_duid); + g_test_add_func ("/dhcp/dhclient/leases/ip4-config/basic", test_read_lease_ip4_config_basic); + g_test_add_func ("/dhcp/dhclient/leases/ip4-config/expired", test_read_lease_ip4_config_expired); + g_test_add_data_func ("/dhcp/dhclient/leases/ip4-config/missing-address", + TESTDIR "/leases/malformed1.leases", + test_read_lease_ip4_config_expect_failure); + g_test_add_data_func ("/dhcp/dhclient/leases/ip4-config/missing-gateway", + TESTDIR "/leases/malformed2.leases", + test_read_lease_ip4_config_expect_failure); + g_test_add_data_func ("/dhcp/dhclient/leases/ip4-config/missing-expire", + TESTDIR "/leases/malformed3.leases", + test_read_lease_ip4_config_expect_failure); + return g_test_run (); } diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index c2442d46ab..44ff71624c 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -55,6 +55,8 @@ typedef struct { NMActiveConnection *master; gboolean master_ready; + gboolean assumed; + NMAuthChain *chain; const char *wifi_shared_permission; NMActiveConnectionAuthResultFunc result_func; @@ -498,6 +500,21 @@ nm_active_connection_set_master (NMActiveConnection *self, NMActiveConnection *m check_master_ready (self); } +void +nm_active_connection_set_assumed (NMActiveConnection *self, gboolean assumed) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + + g_return_if_fail (priv->assumed == FALSE); + priv->assumed = assumed; +} + +gboolean +nm_active_connection_get_assumed (NMActiveConnection *self) +{ + return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->assumed; +} + /****************************************************************/ static void diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h index fa9e5fde4c..074aba9734 100644 --- a/src/nm-active-connection.h +++ b/src/nm-active-connection.h @@ -129,4 +129,9 @@ gboolean nm_active_connection_get_master_ready (NMActiveConnection *self); void nm_active_connection_set_master (NMActiveConnection *self, NMActiveConnection *master); +void nm_active_connection_set_assumed (NMActiveConnection *self, + gboolean assumed); + +gboolean nm_active_connection_get_assumed (NMActiveConnection *self); + #endif /* NM_ACTIVE_CONNECTION_H */ diff --git a/src/nm-connection-provider.c b/src/nm-connection-provider.c index 4a7019441b..39ede1d1e8 100644 --- a/src/nm-connection-provider.c +++ b/src/nm-connection-provider.c @@ -14,6 +14,7 @@ */ #include "nm-connection-provider.h" +#include "nm-utils.h" GSList * nm_connection_provider_get_best_connections (NMConnectionProvider *self, @@ -75,6 +76,25 @@ nm_connection_provider_add_connection (NMConnectionProvider *self, return NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->add_connection (self, connection, save_to_disk, error); } +/** + * nm_connection_provider_get_connection_by_uuid: + * @self: the #NMConnectionProvider + * @uuid: the UUID to search for + * + * Returns: the connection with the given @uuid, or %NULL + */ +NMConnection * +nm_connection_provider_get_connection_by_uuid (NMConnectionProvider *self, + const char *uuid) +{ + g_return_val_if_fail (NM_IS_CONNECTION_PROVIDER (self), NULL); + g_return_val_if_fail (uuid != NULL, NULL); + g_return_val_if_fail (nm_utils_is_uuid (uuid), NULL); + + g_assert (NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->get_connection_by_uuid); + return NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->get_connection_by_uuid (self, uuid); +} + /*****************************************************************************/ static void diff --git a/src/nm-connection-provider.h b/src/nm-connection-provider.h index 2afbc6d65c..05c49a73e6 100644 --- a/src/nm-connection-provider.h +++ b/src/nm-connection-provider.h @@ -65,6 +65,9 @@ struct _NMConnectionProvider { gboolean save_to_disk, GError **error); + NMConnection * (*get_connection_by_uuid) (NMConnectionProvider *self, + const char *uuid); + /* Signals */ void (*connection_added) (NMConnectionProvider *self, NMConnection *connection); @@ -137,4 +140,7 @@ NMConnection *nm_connection_provider_add_connection (NMConnectionProvider *self, gboolean save_to_disk, GError **error); +NMConnection *nm_connection_provider_get_connection_by_uuid (NMConnectionProvider *self, + const char *uuid); + #endif /* NM_CONNECTION_PROVIDER_H */ diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index d74df85832..5229ef9822 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -127,18 +127,44 @@ routes_are_duplicate (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, NMIP4Config * nm_ip4_config_capture (int ifindex) { - NMIP4Config *config = nm_ip4_config_new (); - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4Config *config; + NMIP4ConfigPrivate *priv; + guint i; + gboolean gateway_changed = FALSE; + + /* Slaves have no IP configuration */ + if (nm_platform_link_get_master (ifindex) > 0) + return NULL; + + config = nm_ip4_config_new (); + priv = NM_IP4_CONFIG_GET_PRIVATE (config); g_array_unref (priv->addresses); g_array_unref (priv->routes); priv->addresses = nm_platform_ip4_address_get_all (ifindex); - priv->routes = nm_platform_ip4_route_get_all (ifindex, FALSE); + priv->routes = nm_platform_ip4_route_get_all (ifindex, TRUE); + + /* Extract gateway from default route */ + for (i = 0; i < priv->routes->len; i++) { + const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i); + + if (route->network == 0) { + if (priv->gateway != route->gateway) { + priv->gateway = route->gateway; + gateway_changed = TRUE; + } + /* Remove the default route from the list */ + g_array_remove_index (priv->routes, i); + break; + } + } /* actually, nobody should be connected to the signal, just to be sure, notify */ _NOTIFY (config, PROP_ADDRESSES); _NOTIFY (config, PROP_ROUTES); + if (gateway_changed) + _NOTIFY (config, PROP_GATEWAY); return config; } @@ -314,9 +340,12 @@ nm_ip4_config_update_setting (const NMIP4Config *config, NMSettingIP4Config *set nm_setting_ip4_config_add_address (setting, s_addr); nm_ip4_address_unref (s_addr); } - if (!method) + + /* Only use 'disabled' if the method wasn't previously set */ + if (!method && !nm_setting_ip4_config_get_method (setting)) method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - g_object_set (setting, NM_SETTING_IP4_CONFIG_METHOD, method, NULL); + if (method) + g_object_set (setting, NM_SETTING_IP4_CONFIG_METHOD, method, NULL); /* Routes */ for (i = 0; i < nroutes; i++) { @@ -955,6 +984,22 @@ nm_ip4_config_get_address (const NMIP4Config *config, guint i) return &g_array_index (priv->addresses, NMPlatformIP4Address, i); } +gboolean +nm_ip4_config_address_exists (const NMIP4Config *config, + const NMPlatformIP4Address *needle) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + guint i; + + for (i = 0; i < priv->addresses->len; i++) { + const NMPlatformIP4Address *haystack = &g_array_index (priv->addresses, NMPlatformIP4Address, i); + + if (needle->address == haystack->address && needle->plen == haystack->plen) + return TRUE; + } + return FALSE; +} + /******************************************************************/ void diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index e105098294..3b2b250c93 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -83,6 +83,7 @@ void nm_ip4_config_add_address (NMIP4Config *config, const NMPlatformIP4Address void nm_ip4_config_del_address (NMIP4Config *config, guint i); guint nm_ip4_config_get_num_addresses (const NMIP4Config *config); const NMPlatformIP4Address *nm_ip4_config_get_address (const NMIP4Config *config, guint i); +gboolean nm_ip4_config_address_exists (const NMIP4Config *config, const NMPlatformIP4Address *address); /* Routes */ void nm_ip4_config_reset_routes (NMIP4Config *config); diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 0e434d8126..7522164b37 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -127,18 +127,44 @@ routes_are_duplicate (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, NMIP6Config * nm_ip6_config_capture (int ifindex) { - NMIP6Config *config = nm_ip6_config_new (); - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6Config *config; + NMIP6ConfigPrivate *priv; + guint i; + gboolean gateway_changed = FALSE; + + /* Slaves have no IP configuration */ + if (nm_platform_link_get_master (ifindex) > 0) + return NULL; + + config = nm_ip6_config_new (); + priv = NM_IP6_CONFIG_GET_PRIVATE (config); g_array_unref (priv->addresses); g_array_unref (priv->routes); priv->addresses = nm_platform_ip6_address_get_all (ifindex); - priv->routes = nm_platform_ip6_route_get_all (ifindex, FALSE); + priv->routes = nm_platform_ip6_route_get_all (ifindex, TRUE); + + /* Extract gateway from default route */ + for (i = 0; i < priv->routes->len; i++) { + const NMPlatformIP6Route *route = &g_array_index (priv->routes, NMPlatformIP6Route, i); + + if (IN6_IS_ADDR_UNSPECIFIED (&route->network)) { + if (!IN6_ARE_ADDR_EQUAL (&priv->gateway, &route->gateway)) { + priv->gateway = route->gateway; + gateway_changed = TRUE; + } + /* Remove the default route from the list */ + g_array_remove_index (priv->routes, i); + break; + } + } /* actually, nobody should be connected to the signal, just to be sure, notify */ _NOTIFY (config, PROP_ADDRESSES); _NOTIFY (config, PROP_ROUTES); + if (gateway_changed) + _NOTIFY (config, PROP_GATEWAY); return config; } @@ -285,8 +311,11 @@ nm_ip6_config_update_setting (const NMIP6Config *config, NMSettingIP6Config *set NMIP6Address *s_addr; /* Ignore link-local address. */ - if (IN6_IS_ADDR_LINKLOCAL (&address->address)) + if (IN6_IS_ADDR_LINKLOCAL (&address->address)) { + if (!method) + method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; continue; + } /* Detect dynamic address */ if (address->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { @@ -295,7 +324,7 @@ nm_ip6_config_update_setting (const NMIP6Config *config, NMSettingIP6Config *set } /* Static address found. */ - if (!method) + if (!method || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; s_addr = nm_ip6_address_new (); @@ -308,9 +337,12 @@ nm_ip6_config_update_setting (const NMIP6Config *config, NMSettingIP6Config *set nm_setting_ip6_config_add_address (setting, s_addr); nm_ip6_address_unref (s_addr); } - if (!method) - method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; - g_object_set (setting, NM_SETTING_IP6_CONFIG_METHOD, method, NULL); + + /* Only use 'ignore' if the method wasn't previously set */ + if (!method && !nm_setting_ip6_config_get_method (setting)) + method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; + if (method) + g_object_set (setting, NM_SETTING_IP6_CONFIG_METHOD, method, NULL); /* Routes */ for (i = 0; i < nroutes; i++) { @@ -854,6 +886,23 @@ nm_ip6_config_get_address (const NMIP6Config *config, guint i) return &g_array_index (priv->addresses, NMPlatformIP6Address, i); } +gboolean +nm_ip6_config_address_exists (const NMIP6Config *config, + const NMPlatformIP6Address *needle) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + guint i; + + for (i = 0; i < priv->addresses->len; i++) { + const NMPlatformIP6Address *haystack = &g_array_index (priv->addresses, NMPlatformIP6Address, i); + + if ( IN6_ARE_ADDR_EQUAL (&needle->address, &haystack->address) + && needle->plen == haystack->plen) + return TRUE; + } + return FALSE; +} + /******************************************************************/ void diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index 992a7cdd3e..538490a78c 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -82,6 +82,7 @@ void nm_ip6_config_add_address (NMIP6Config *config, const NMPlatformIP6Address void nm_ip6_config_del_address (NMIP6Config *config, guint i); guint nm_ip6_config_get_num_addresses (const NMIP6Config *config); const NMPlatformIP6Address *nm_ip6_config_get_address (const NMIP6Config *config, guint i); +gboolean nm_ip6_config_address_exists (const NMIP6Config *config, const NMPlatformIP6Address *address); /* Routes */ void nm_ip6_config_reset_routes (NMIP6Config *config); diff --git a/src/nm-manager.c b/src/nm-manager.c index e97284e141..313b67bbad 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -151,7 +151,7 @@ static void bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr, const char *object_path, gpointer user_data); -static void add_device (NMManager *self, NMDevice *device); +static void add_device (NMManager *self, NMDevice *device, gboolean nm_created); static void remove_device (NMManager *self, NMDevice *device, gboolean quitting); static void hostname_provider_init (NMHostnameProvider *provider_class); @@ -166,6 +166,7 @@ static NMActiveConnection *_new_active_connection (NMManager *self, static void policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data); static NMDevice *find_device_by_ip_iface (NMManager *self, const gchar *iface); +static NMDevice *find_device_by_iface (NMManager *self, const gchar *iface); static void rfkill_change_wifi (const char *desc, gboolean enabled); @@ -176,6 +177,14 @@ platform_link_added_cb (NMPlatform *platform, NMPlatformReason reason, gpointer user_data); +static gboolean find_master (NMManager *self, + NMConnection *connection, + NMDevice *device, + NMConnection **out_master_connection, + NMDevice **out_master_device); + +static void nm_manager_update_state (NMManager *manager); + #define SSD_POKE_INTERVAL 120 #define ORIGDEV_TAG "originating-device" @@ -241,8 +250,6 @@ typedef struct { guint timestamp_update_id; - GHashTable *nm_bridges; - /* Track auto-activation for software devices */ GHashTable *noauto_sw_devices; @@ -372,6 +379,8 @@ active_connection_state_changed (NMActiveConnection *active, if (!priv->ac_cleanup_id) priv->ac_cleanup_id = g_idle_add (_active_connection_cleanup, self); } + + nm_manager_update_state (self); } static void @@ -572,7 +581,7 @@ modem_added (NMModemManager *modem_manager, /* Make the new modem device */ device = nm_device_modem_new (modem, driver); if (device) - add_device (self, device); + add_device (self, device, TRUE); } static void @@ -640,12 +649,59 @@ checked_connectivity (GObject *object, GAsyncResult *result, gpointer user_data) g_object_unref (manager); } +static NMState +find_best_device_state (NMManager *manager, gboolean *want_connectivity_check) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + NMState best_state = NM_STATE_DISCONNECTED; + GSList *iter; + + for (iter = priv->active_connections; iter; iter = iter->next) { + NMActiveConnection *ac = NM_ACTIVE_CONNECTION (iter->data); + NMActiveConnectionState ac_state = nm_active_connection_get_state (ac); + + switch (ac_state) { + case NM_ACTIVE_CONNECTION_STATE_ACTIVATED: + if ( nm_active_connection_get_default (ac) + || nm_active_connection_get_default6 (ac)) { + nm_connectivity_set_online (priv->connectivity, TRUE); + if (nm_connectivity_get_state (priv->connectivity) == NM_CONNECTIVITY_FULL) { + *want_connectivity_check = FALSE; + return NM_STATE_CONNECTED_GLOBAL; + } + + best_state = NM_STATE_CONNECTING; + *want_connectivity_check = TRUE; + } else { + if (best_state < NM_STATE_CONNECTING) + best_state = NM_STATE_CONNECTED_LOCAL; + } + break; + case NM_ACTIVE_CONNECTION_STATE_ACTIVATING: + if (!nm_active_connection_get_assumed (ac)) { + if (best_state != NM_STATE_CONNECTED_GLOBAL) + best_state = NM_STATE_CONNECTING; + } + break; + case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING: + if (!nm_active_connection_get_assumed (ac)) { + if (best_state < NM_STATE_DISCONNECTING) + best_state = NM_STATE_DISCONNECTING; + } + break; + default: + break; + } + } + + return best_state; +} + static void nm_manager_update_state (NMManager *manager) { NMManagerPrivate *priv; NMState new_state = NM_STATE_DISCONNECTED; - GSList *iter; gboolean want_connectivity_check = FALSE; g_return_if_fail (NM_IS_MANAGER (manager)); @@ -654,30 +710,8 @@ nm_manager_update_state (NMManager *manager) if (manager_sleeping (manager)) new_state = NM_STATE_ASLEEP; - else { - for (iter = priv->devices; iter; iter = iter->next) { - NMDevice *dev = NM_DEVICE (iter->data); - NMDeviceState state = nm_device_get_state (dev); - - if (state == NM_DEVICE_STATE_ACTIVATED) { - nm_connectivity_set_online (priv->connectivity, TRUE); - if (nm_connectivity_get_state (priv->connectivity) != NM_CONNECTIVITY_FULL) { - new_state = NM_STATE_CONNECTING; - want_connectivity_check = TRUE; - } else { - new_state = NM_STATE_CONNECTED_GLOBAL; - break; - } - } - - if (nm_device_is_activating (dev)) - new_state = NM_STATE_CONNECTING; - else if (new_state != NM_STATE_CONNECTING) { - if (state == NM_DEVICE_STATE_DEACTIVATING) - new_state = NM_STATE_DISCONNECTING; - } - } - } + else + new_state = find_best_device_state (manager, &want_connectivity_check); if (new_state == NM_STATE_CONNECTING && want_connectivity_check) { nm_connectivity_check_async (priv->connectivity, @@ -710,8 +744,6 @@ manager_device_state_changed (NMDevice *device, default: break; } - - nm_manager_update_state (self); } static void device_has_pending_action_changed (NMDevice *device, @@ -764,18 +796,20 @@ remove_device (NMManager *manager, NMDevice *device, gboolean quitting) NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); if (nm_device_get_managed (device)) { - /* When quitting, we want to leave up interfaces & connections - * that can be taken over again (ie, "assumed") when NM restarts - * so that '/etc/init.d/NetworkManager restart' will not distrupt - * networking for interfaces that support connection assumption. - * All other devices get unmanaged when NM quits so that their - * connections get torn down and the interface is deactivated. + /* Leave configured interfaces up when quitting so they can be + * taken over again if NM starts up, and to ensure connectivity while + * NM is gone. Assumed connections don't get taken down even if they + * haven't been fully activated. */ if ( !nm_device_can_assume_connections (device) || (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED) - || !quitting) - nm_device_set_manager_managed (device, FALSE, NM_DEVICE_STATE_REASON_REMOVED); + || !quitting) { + NMActRequest *req = nm_device_get_act_request (device); + + if (!req || !nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (req))) + nm_device_set_manager_managed (device, FALSE, NM_DEVICE_STATE_REASON_REMOVED); + } } g_signal_handlers_disconnect_matched (device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, manager); @@ -1096,90 +1130,6 @@ connection_needs_virtual_device (NMConnection *connection) /***************************/ -/* FIXME: remove when we handle bridges non-destructively */ - -#define NM_BRIDGE_FILE NMRUNDIR "/nm-bridges" - -static void -read_nm_created_bridges (NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - char *contents; - char **lines, **iter; - GTimeVal tv; - glong ts; - - if (!g_file_get_contents (NM_BRIDGE_FILE, &contents, NULL, NULL)) - return; - - g_get_current_time (&tv); - - lines = g_strsplit_set (contents, "\n", 0); - g_free (contents); - - for (iter = lines; iter && *iter; iter++) { - if (g_str_has_prefix (*iter, "ts=")) { - errno = 0; - ts = strtol (*iter + 3, NULL, 10); - /* allow 30 minutes time difference before we ignore the file */ - if (errno || ABS (tv.tv_sec - ts) > 1800) - goto out; - } else if (g_str_has_prefix (*iter, "iface=")) - g_hash_table_insert (priv->nm_bridges, g_strdup (*iter + 6), GUINT_TO_POINTER (1)); - } - -out: - g_strfreev (lines); - unlink (NM_BRIDGE_FILE); -} - -static void -write_nm_created_bridges (NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GString *br_list; - GSList *iter; - GError *error = NULL; - GTimeVal tv; - gboolean found = FALSE; - - /* write out nm-created bridges list */ - br_list = g_string_sized_new (50); - - /* Timestamp is first line */ - g_get_current_time (&tv); - g_string_append_printf (br_list, "ts=%ld\n", tv.tv_sec); - - for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - NMDevice *device = iter->data; - - if (nm_device_get_device_type (device) == NM_DEVICE_TYPE_BRIDGE) { - g_string_append_printf (br_list, "iface=%s\n", nm_device_get_iface (device)); - found = TRUE; - } - } - - if (found) { - if (!g_file_set_contents (NM_BRIDGE_FILE, br_list->str, -1, &error)) { - nm_log_warn (LOGD_BRIDGE, "Failed to write NetworkManager-created bridge list; " - "on restart bridges may not be recognized. (%s)", - error ? error->message : "unknown"); - g_clear_error (&error); - } - } - g_string_free (br_list, TRUE); -} - -static gboolean -bridge_created_by_nm (NMManager *self, const char *iface) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - - return (priv->nm_bridges && g_hash_table_lookup (priv->nm_bridges, iface)); -} - -/***************************/ - /** * system_create_virtual_device: * @self: the #NMManager @@ -1229,12 +1179,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) } else if (nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME)) { device = nm_device_team_new_for_connection (connection); } else if (nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) { - /* FIXME: remove when we handle bridges non-destructively */ - if (nm_platform_link_get_ifindex (iface) > 0 && !bridge_created_by_nm (self, iface)) { - nm_log_warn (LOGD_DEVICE, "(%s): cannot use existing bridge for '%s'", - iface, nm_connection_get_id (connection)); - } else - device = nm_device_bridge_new_for_connection (connection); + device = nm_device_bridge_new_for_connection (connection); } else if (nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)) { device = nm_device_vlan_new_for_connection (connection, parent); } else if (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) { @@ -1243,7 +1188,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) if (device) { nm_device_set_is_nm_owned (device, TRUE); - add_device (self, device); + add_device (self, device, TRUE); } g_signal_handlers_unblock_by_func (nm_platform_get (), G_CALLBACK (platform_link_added_cb), self); @@ -1756,24 +1701,15 @@ local_slist_free (void *loc) } /** - * get_connection: + * get_existing_connection: * @manager: #NMManager instance * @device: #NMDevice instance * - * Returns one of the following: - * - * 1) An existing #NMSettingsConnection to be assumed. - * - * 2) A generated #NMConnection to be assumed. You can distinguish this - * case using NM_IS_SETTINGS_CONNECTION(). - * - * 3) %NULL when no connection was detected or the @device doesn't support - * generating connections. - * - * Supports both nm-device's match_l2_config() and update_connection(). + * Returns: a #NMSettingsConnection to be assumed by the device, or %NULL if + * the device does not support assuming existing connections. */ static NMConnection * -get_connection (NMManager *manager, NMDevice *device) +get_existing_connection (NMManager *manager, NMDevice *device) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); free_slist GSList *connections = nm_manager_get_activatable_connections (manager); @@ -1782,24 +1718,7 @@ get_connection (NMManager *manager, NMDevice *device) GSList *iter; GError *error = NULL; - /* We still support the older API to match a NMDevice object to an - * existing connection using nm_device_find_assumable_connection(). - * - * When the older API is still available for a particular device - * type, we use it. To opt for the newer interface, the NMDevice - * subclass must omit the match_l2_config virtual function - * implementation. - */ - if (NM_DEVICE_GET_CLASS (device)->match_l2_config) { - NMConnection *candidate = nm_device_find_assumable_connection (device, connections); - - if (candidate) { - nm_log_info (LOGD_DEVICE, "(%s): Found matching connection '%s' (legacy API)", - nm_device_get_iface (device), - nm_connection_get_id (candidate)); - return candidate; - } - } + nm_device_capture_initial_config (device); /* The core of the API is nm_device_generate_connection() function and * update_connection() virtual method and the convenient connection_type @@ -1808,11 +1727,8 @@ get_connection (NMManager *manager, NMDevice *device) * returns NULL. */ connection = nm_device_generate_connection (device); - if (!connection) { - nm_log_info (LOGD_DEVICE, "(%s): No existing connection detected.", - nm_device_get_iface (device)); + if (!connection) return NULL; - } /* Now we need to compare the generated connection to each configured * connection. The comparison function is the heart of the connection @@ -1827,7 +1743,7 @@ get_connection (NMManager *manager, NMDevice *device) NMConnection *candidate = NM_CONNECTION (iter->data); if (nm_connection_compare (connection, candidate, NM_SETTING_COMPARE_FLAG_CANDIDATE)) { - nm_log_info (LOGD_DEVICE, "(%s): Found matching connection: '%s'", + nm_log_info (LOGD_DEVICE, "(%s): found matching connection '%s'", nm_device_get_iface (device), nm_connection_get_id (candidate)); g_object_unref (connection); @@ -1835,9 +1751,9 @@ get_connection (NMManager *manager, NMDevice *device) } } - nm_log_info (LOGD_DEVICE, "(%s): Using generated connection: '%s'", - nm_device_get_iface (device), - nm_connection_get_id (connection)); + nm_log_dbg (LOGD_DEVICE, "(%s): generated connection '%s'", + nm_device_get_iface (device), + nm_connection_get_id (connection)); added = nm_settings_add_connection (priv->settings, connection, FALSE, &error); if (!added) { @@ -1853,14 +1769,14 @@ get_connection (NMManager *manager, NMDevice *device) } static void -add_device (NMManager *self, NMDevice *device) +add_device (NMManager *self, NMDevice *device, gboolean nm_created) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); const char *iface, *driver, *type_desc; char *path; static guint32 devcount = 0; const GSList *unmanaged_specs; - NMConnection *connection; + NMConnection *connection = NULL; gboolean enabled = FALSE; RfKillType rtype; NMDeviceType devtype; @@ -1946,7 +1862,9 @@ add_device (NMManager *self, NMDevice *device) nm_log_info (LOGD_CORE, "(%s): exported as %s", iface, path); g_free (path); - connection = get_connection (self, device); + /* Don't bother generating a connection for devices NM just created */ + if (!nm_created) + connection = get_existing_connection (self, device); /* Start the device if it's supposed to be managed */ unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings); @@ -1967,9 +1885,7 @@ add_device (NMManager *self, NMDevice *device) system_create_virtual_devices (self); /* If the device has a connection it can assume, do that now */ - if ( connection - && nm_device_is_available (device) - && nm_device_connection_is_available (device, connection)) { + if (connection) { NMActiveConnection *active; NMAuthSubject *subject; GError *error = NULL; @@ -1985,6 +1901,22 @@ add_device (NMManager *self, NMDevice *device) subject = nm_auth_subject_new_internal (); active = _new_active_connection (self, connection, NULL, device, subject, &error); if (active) { + NMDevice *master = NULL; + NMActRequest *master_req; + + /* If the device is a slave or VLAN, find the master ActiveConnection */ + if (find_master (self, connection, device, NULL, &master) && master) { + master_req = nm_device_get_act_request (master); + if (master_req) + nm_active_connection_set_master (active, NM_ACTIVE_CONNECTION (master_req)); + else { + nm_log_warn (LOGD_DEVICE, "(%s): master device %s not activating!", + nm_device_get_iface (device), + nm_device_get_iface (master)); + } + } + + nm_active_connection_set_assumed (active, TRUE); nm_active_connection_export (active); active_connection_add (self, active); nm_device_activate (device, NM_ACT_REQUEST (active)); @@ -2031,7 +1963,7 @@ bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr, has_dun && has_nap ? " " : "", has_nap ? "NAP" : ""); - add_device (manager, device); + add_device (manager, device, TRUE); } } @@ -2277,11 +2209,7 @@ platform_link_added_cb (NMPlatform *platform, device = nm_device_team_new (plink); break; case NM_LINK_TYPE_BRIDGE: - /* FIXME: always create device when we handle bridges non-destructively */ - if (bridge_created_by_nm (self, plink->name)) - device = nm_device_bridge_new (plink); - else - nm_log_info (LOGD_BRIDGE, "(%s): ignoring bridge not created by NetworkManager", plink->name); + device = nm_device_bridge_new (plink); break; case NM_LINK_TYPE_VLAN: /* Have to find the parent device */ @@ -2335,7 +2263,7 @@ platform_link_added_cb (NMPlatform *platform, } if (device) - add_device (self, device); + add_device (self, device, FALSE); } static void @@ -2372,7 +2300,7 @@ atm_device_added_cb (NMAtmManager *atm_mgr, device = nm_device_adsl_new (sysfs_path, iface, driver); if (device) - add_device (self, device); + add_device (self, device, FALSE); } static void @@ -4157,13 +4085,6 @@ nm_manager_start (NMManager *self) system_unmanaged_devices_changed_cb (priv->settings, NULL, self); system_hostname_changed_cb (priv->settings, NULL, self); - /* FIXME: remove when we handle bridges non-destructively */ - /* Read a list of bridges NM managed when it last quit, and only - * manage those bridges to avoid conflicts with external tools. - */ - priv->nm_bridges = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - read_nm_created_bridges (self); - nm_platform_query_devices (); nm_atm_manager_query_devices (priv->atm_mgr); nm_bluez_manager_query_devices (priv->bluez_mgr); @@ -4174,10 +4095,6 @@ nm_manager_start (NMManager *self) */ system_create_virtual_devices (self); - /* FIXME: remove when we handle bridges non-destructively */ - g_hash_table_unref (priv->nm_bridges); - priv->nm_bridges = NULL; - check_if_startup_complete (self); } @@ -4623,9 +4540,6 @@ dispose (GObject *object) nm_auth_changed_func_unregister (authority_changed_cb, manager); - /* FIXME: remove when we handle bridges non-destructively */ - write_nm_created_bridges (manager); - /* Remove all devices */ while (priv->devices) remove_device (manager, NM_DEVICE (priv->devices->data), TRUE); diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index e3d6357706..a35b0cc906 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1060,14 +1060,18 @@ refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed announce_object (platform, cached_object, REMOVED, reason); } } else { - g_return_val_if_fail (kernel_object, FALSE); + if (!kernel_object) + return FALSE; hack_empty_master_iff_lower_up (platform, kernel_object); if (cached_object) nl_cache_remove (cached_object); nle = nl_cache_add (cache, kernel_object); - g_return_val_if_fail (!nle, FALSE); + if (nle) { + nm_log_dbg (LOGD_PLATFORM, "refresh_object(reason %d) failed during nl_cache_add with %d", reason, nle); + return FALSE; + } announce_object (platform, kernel_object, cached_object ? CHANGED : ADDED, reason); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 3f645ed90b..f132f2997b 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -267,37 +267,6 @@ nm_platform_query_devices (void) g_array_unref (links_array); } -static int -compare_links (gconstpointer a, gconstpointer b) -{ - NMPlatformLink *link_a = (NMPlatformLink *) a; - NMPlatformLink *link_b = (NMPlatformLink *) b; - int sortindex_a, sortindex_b; - - /* We mostly want to sort by ifindex. However, slaves should sort - * before their masters, and children (eg, VLANs) should sort after - * their parents. - */ - if (link_a->master) - sortindex_a = link_a->master * 3 - 1; - else if (link_a->parent) - sortindex_a = link_a->parent * 3 + 1; - else - sortindex_a = link_a->ifindex * 3; - - if (link_b->master) - sortindex_b = link_b->master * 3 - 1; - else if (link_b->parent) - sortindex_b = link_b->parent * 3 + 1; - else - sortindex_b = link_b->ifindex * 3; - - if (sortindex_a == sortindex_b) - return link_a->ifindex - link_b->ifindex; - else - return sortindex_a - sortindex_b; -} - /** * nm_platform_link_get_all: * @@ -307,15 +276,100 @@ compare_links (gconstpointer a, gconstpointer b) GArray * nm_platform_link_get_all (void) { - GArray *links; + GArray *links, *result; + guint i, j, nresult; + GHashTable *unseen; + NMPlatformLink *item; reset_error (); g_return_val_if_fail (klass->link_get_all, NULL); links = klass->link_get_all (platform); - g_array_sort (links, compare_links); - return links; + + if (!links || links->len == 0) + return links; + + unseen = g_hash_table_new (g_direct_hash, g_direct_equal); + for (i = 0; i < links->len; i++) { + item = &g_array_index (links, NMPlatformLink, i); + + if (item->ifindex <= 0 || g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex))) { + g_warn_if_reached (); + item->ifindex = 0; + continue; + } + + g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL); + } + +#ifndef G_DISABLE_ASSERT + /* Ensure that link_get_all returns a consistent and valid result. */ + for (i = 0; i < links->len; i++) { + item = &g_array_index (links, NMPlatformLink, i); + + if (!item->ifindex) + continue; + if (item->master != 0) { + g_warn_if_fail (item->master > 0); + g_warn_if_fail (item->master != item->ifindex); + g_warn_if_fail (g_hash_table_contains (unseen, GINT_TO_POINTER (item->master))); + } + if (item->parent != 0) { + g_warn_if_fail (item->parent > 0); + g_warn_if_fail (item->parent != item->ifindex); + g_warn_if_fail (g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent))); + } + } +#endif + + /* Re-order the links list such that children/slaves come after all ancestors */ + nresult = g_hash_table_size (unseen); + result = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformLink), nresult); + g_array_set_size (result, nresult); + + j = 0; + do { + gboolean found_something = FALSE; + guint first_idx = G_MAXUINT; + + for (i = 0; i < links->len; i++) { + item = &g_array_index (links, NMPlatformLink, i); + + if (!item->ifindex) + continue; + + if (first_idx == G_MAXUINT) + first_idx = i; + + g_assert (g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex))); + + if (item->master > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->master))) + continue; + if (item->parent > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent))) + continue; + + g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); + g_array_index (result, NMPlatformLink, j++) = *item; + item->ifindex = 0; + found_something = TRUE; + } + + if (!found_something) { + /* there is a circle, pop the first (remaining) element from the list */ + g_warn_if_reached (); + item = &g_array_index (links, NMPlatformLink, first_idx); + + g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); + g_array_index (result, NMPlatformLink, j++) = *item; + item->ifindex = 0; + } + } while (j < nresult); + + g_hash_table_destroy (unseen); + g_array_free (links, TRUE); + + return result; } /** diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 7de0280c94..9cafbc7be7 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -124,16 +124,16 @@ typedef struct { in_addr_t address; int plen; guint32 timestamp; - guint32 lifetime; - guint32 preferred; + guint32 lifetime; /* seconds */ + guint32 preferred; /* seconds */ } NMPlatformIP4Address; typedef struct { int ifindex; struct in6_addr address; int plen; - guint32 timestamp; - guint32 lifetime; + guint32 timestamp; /* seconds */ + guint32 lifetime; /* seconds */ guint32 preferred; guint flags; /* ifa_flags from <linux/if_addr.h>, field type "unsigned int" is as used in rtnl_addr_get_flags. */ } NMPlatformIP6Address; diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index c5f68ddd48..49d64d1f6b 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -1730,6 +1730,12 @@ has_connections_loaded (NMConnectionProvider *provider) return priv->connections_loaded; } +static NMConnection * +cp_get_connection_by_uuid (NMConnectionProvider *provider, const char *uuid) +{ + return NM_CONNECTION (nm_settings_get_connection_by_uuid (NM_SETTINGS (provider), uuid)); +} + /***************************************************************/ NMSettings * @@ -1765,6 +1771,7 @@ connection_provider_init (NMConnectionProvider *cp_class) cp_class->get_connections = get_connections; cp_class->has_connections_loaded = has_connections_loaded; cp_class->add_connection = _nm_connection_provider_add_connection; + cp_class->get_connection_by_uuid = cp_get_connection_by_uuid; } static void diff --git a/src/settings/plugins/ifcfg-rh/plugin.c b/src/settings/plugins/ifcfg-rh/plugin.c index c51afbee39..ea0ddf34e2 100644 --- a/src/settings/plugins/ifcfg-rh/plugin.c +++ b/src/settings/plugins/ifcfg-rh/plugin.c @@ -212,7 +212,7 @@ find_by_path (SCPluginIfcfg *self, const char *path) g_hash_table_iter_init (&iter, priv->connections); while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) { - if (g_str_equal (path, nm_ifcfg_connection_get_path (candidate))) + if (g_strcmp0 (path, nm_ifcfg_connection_get_path (candidate)) == 0) return candidate; } return NULL; @@ -445,8 +445,11 @@ read_connections (SCPluginIfcfg *plugin) oldconns = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, &value)) - g_hash_table_insert (oldconns, g_strdup (nm_ifcfg_connection_get_path (value)), value); + while (g_hash_table_iter_next (&iter, NULL, &value)) { + const char *ifcfg_path = nm_ifcfg_connection_get_path (value); + if (ifcfg_path) + g_hash_table_insert (oldconns, g_strdup (ifcfg_path), value); + } while ((item = g_dir_read_name (dir))) { char *full_path, *old_path; |