diff --git a/introspection/nm-manager.xml b/introspection/nm-manager.xml index 4fc7692cc4..a3d6647926 100644 --- a/introspection/nm-manager.xml +++ b/introspection/nm-manager.xml @@ -315,6 +315,25 @@ + + + The object path of the "primary" active connection being used + to access the network. In particular, if there is no VPN + active, or the VPN does not have the default route, then this + indicates the connection that has the default route. If there + is a VPN active with the default route, then this indicates + the connection that contains the route to the VPN endpoint. + + + + + + The object path of an active connection that is currently + being activated and which is expected to become the new + PrimaryConnection when it finishes activating. + + + Indicates whether NM is still starting up; this becomes FALSE diff --git a/src/nm-manager.c b/src/nm-manager.c index a1b5677b51..bc83da5734 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -217,6 +217,8 @@ typedef struct { GSList *active_connections; guint ac_cleanup_id; + NMActiveConnection *primary_connection; + NMActiveConnection *activating_connection; GSList *devices; NMState state; @@ -298,6 +300,8 @@ enum { PROP_WIMAX_HARDWARE_ENABLED, PROP_ACTIVE_CONNECTIONS, PROP_CONNECTIVITY, + PROP_PRIMARY_CONNECTION, + PROP_ACTIVATING_CONNECTION, /* Not exported */ PROP_HOSTNAME, @@ -4179,6 +4183,68 @@ firmware_dir_changed (GFileMonitor *monitor, } } +static void +policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *best; + NMActiveConnection *ac; + + /* Note: this assumes that it's not possible for the IP4 default + * route to be going over the default-ip6-device. If that changes, + * we need something more complicated here. + */ + best = nm_policy_get_default_ip4_device (priv->policy); + if (!best) + best = nm_policy_get_default_ip6_device (priv->policy); + + if (best) + ac = NM_ACTIVE_CONNECTION (nm_device_get_act_request (best)); + else + ac = NULL; + + if (ac != priv->primary_connection) { + g_clear_object (&priv->primary_connection); + priv->primary_connection = ac ? g_object_ref (ac) : NULL; + nm_log_dbg (LOGD_CORE, "PrimaryConnection now %s", ac ? nm_active_connection_get_name (ac) : "(none)"); + g_object_notify (G_OBJECT (self), NM_MANAGER_PRIMARY_CONNECTION); + } +} + +static void +policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *activating, *best; + NMActiveConnection *ac; + + /* We only look at activating-ip6-device if activating-ip4-device + * AND default-ip4-device are NULL; if default-ip4-device is + * non-NULL, then activating-ip6-device is irrelevant, since while + * that device might become the new default-ip6-device, it can't + * become primary-connection while default-ip4-device is set to + * something else. + */ + activating = nm_policy_get_activating_ip4_device (priv->policy); + best = nm_policy_get_default_ip4_device (priv->policy); + if (!activating && !best) + activating = nm_policy_get_activating_ip6_device (priv->policy); + + if (activating) + ac = NM_ACTIVE_CONNECTION (nm_device_get_act_request (activating)); + else + ac = NULL; + + if (ac != priv->activating_connection) { + g_clear_object (&priv->activating_connection); + priv->activating_connection = ac ? g_object_ref (ac) : NULL; + nm_log_dbg (LOGD_CORE, "ActivatingConnection now %s", ac ? nm_active_connection_get_name (ac) : "(none)"); + g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVATING_CONNECTION); + } +} + #define NM_PERM_DENIED_ERROR "org.freedesktop.NetworkManager.PermissionDenied" #define DEV_PERM_DENIED_ERROR "org.freedesktop.NetworkManager.Device.PermissionDenied" @@ -4360,6 +4426,14 @@ nm_manager_new (NMSettings *settings, priv = NM_MANAGER_GET_PRIVATE (singleton); priv->policy = nm_policy_new (singleton, settings); + g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP4_DEVICE, + G_CALLBACK (policy_default_device_changed), singleton); + g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP6_DEVICE, + G_CALLBACK (policy_default_device_changed), singleton); + g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP4_DEVICE, + G_CALLBACK (policy_activating_device_changed), singleton); + g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP6_DEVICE, + G_CALLBACK (policy_activating_device_changed), singleton); priv->connectivity = nm_connectivity_new (); g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE, @@ -4491,11 +4565,15 @@ dispose (GObject *object) g_object_unref (iter->data); } g_slist_free (priv->active_connections); + g_clear_object (&priv->primary_connection); + g_clear_object (&priv->activating_connection); g_clear_object (&priv->connectivity); g_free (priv->hostname); + g_signal_handlers_disconnect_by_func (priv->policy, G_CALLBACK (policy_default_device_changed), singleton); + g_signal_handlers_disconnect_by_func (priv->policy, G_CALLBACK (policy_activating_device_changed), singleton); g_object_unref (priv->policy); g_object_unref (priv->settings); @@ -4739,6 +4817,14 @@ get_property (GObject *object, guint prop_id, case PROP_CONNECTIVITY: g_value_set_uint (value, nm_connectivity_get_state (priv->connectivity)); break; + case PROP_PRIMARY_CONNECTION: + path = priv->primary_connection ? nm_active_connection_get_path (priv->primary_connection) : "/"; + g_value_set_boxed (value, path); + break; + case PROP_ACTIVATING_CONNECTION: + path = priv->activating_connection ? nm_active_connection_get_path (priv->activating_connection) : "/"; + g_value_set_boxed (value, path); + break; case PROP_HOSTNAME: g_value_set_string (value, priv->hostname); break; @@ -5011,6 +5097,22 @@ nm_manager_class_init (NMManagerClass *manager_class) NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_PRIMARY_CONNECTION, + g_param_spec_boxed (NM_MANAGER_PRIMARY_CONNECTION, + "Primary connection", + "Primary connection", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_ACTIVATING_CONNECTION, + g_param_spec_boxed (NM_MANAGER_ACTIVATING_CONNECTION, + "Activating connection", + "Activating connection", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READABLE)); + /* Hostname is not exported over D-Bus */ g_object_class_install_property (object_class, PROP_HOSTNAME, diff --git a/src/nm-manager.h b/src/nm-manager.h index 79165f0f4c..e972644e6d 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -61,6 +61,8 @@ typedef enum { #define NM_MANAGER_WIMAX_HARDWARE_ENABLED "wimax-hardware-enabled" #define NM_MANAGER_ACTIVE_CONNECTIONS "active-connections" #define NM_MANAGER_CONNECTIVITY "connectivity" +#define NM_MANAGER_PRIMARY_CONNECTION "primary-connection" +#define NM_MANAGER_ACTIVATING_CONNECTION "activating-connection" /* Not exported */ #define NM_MANAGER_HOSTNAME "hostname" diff --git a/src/nm-policy.c b/src/nm-policy.c index d65dd54096..cb60560b6a 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -59,8 +59,8 @@ typedef struct { NMSettings *settings; - NMDevice *default_device4; - NMDevice *default_device6; + NMDevice *default_device4, *activating_device4; + NMDevice *default_device6, *activating_device6; GResolver *resolver; GInetAddress *lookup_addr; @@ -79,6 +79,15 @@ typedef struct { G_DEFINE_TYPE (NMPolicy, nm_policy, G_TYPE_OBJECT) +enum { + PROP_0, + + PROP_DEFAULT_IP4_DEVICE, + PROP_DEFAULT_IP6_DEVICE, + PROP_ACTIVATING_IP4_DEVICE, + PROP_ACTIVATING_IP6_DEVICE +}; + #define RETRIES_TAG "autoconnect-retries" #define RETRIES_DEFAULT 4 #define RESET_RETRIES_TIMESTAMP_TAG "reset-retries-timestamp-tag" @@ -89,7 +98,7 @@ static void schedule_activate_all (NMPolicy *policy); static NMDevice * -get_best_ip4_device (NMManager *manager) +get_best_ip4_device (NMManager *manager, gboolean fully_activated) { GSList *devices, *iter; NMDevice *best = NULL; @@ -101,6 +110,7 @@ get_best_ip4_device (NMManager *manager) for (iter = devices; iter; iter = g_slist_next (iter)) { NMDevice *dev = NM_DEVICE (iter->data); NMDeviceType devtype = nm_device_get_device_type (dev); + NMDeviceState state = nm_device_get_state (dev); NMActRequest *req; NMConnection *connection; NMIP4Config *ip4_config; @@ -108,12 +118,23 @@ get_best_ip4_device (NMManager *manager) int prio; const char *method = NULL; - if ( nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED - && nm_device_get_state (dev) != NM_DEVICE_STATE_SECONDARIES) + if ( state <= NM_DEVICE_STATE_DISCONNECTED + || state >= NM_DEVICE_STATE_DEACTIVATING) + continue; + + if (fully_activated && state < NM_DEVICE_STATE_SECONDARIES) continue; ip4_config = nm_device_get_ip4_config (dev); - if (!ip4_config) + if (ip4_config) { + /* Make sure the device has a gateway */ + if (!nm_ip4_config_get_gateway (ip4_config) && (devtype != NM_DEVICE_TYPE_MODEM)) + continue; + + /* 'never-default' devices can't ever be the default */ + if (nm_ip4_config_get_never_default (ip4_config)) + continue; + } else if (fully_activated) continue; req = nm_device_get_act_request (dev); @@ -121,22 +142,17 @@ get_best_ip4_device (NMManager *manager) connection = nm_act_request_get_connection (req); g_assert (connection); - /* Never set the default route through an IPv4LL-addressed device */ s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (s_ip4) + if (s_ip4) { + /* Never set the default route through an IPv4LL-addressed device */ method = nm_setting_ip4_config_get_method (s_ip4); + if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) + continue; - if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) - continue; - - /* Make sure the device has a gateway */ - if (!nm_ip4_config_get_gateway (ip4_config) && (devtype != NM_DEVICE_TYPE_MODEM)) - continue; - - /* 'never-default' devices can't ever be the default */ - if ( (s_ip4 && nm_setting_ip4_config_get_never_default (s_ip4)) - || nm_ip4_config_get_never_default (ip4_config)) - continue; + /* 'never-default' devices can't ever be the default */ + if (nm_setting_ip4_config_get_never_default (s_ip4)) + continue; + } prio = nm_device_get_priority (dev); if (prio > 0 && prio < best_prio) { @@ -145,11 +161,25 @@ get_best_ip4_device (NMManager *manager) } } + if (!best) + return NULL; + + if (!fully_activated) { + NMDeviceState state = nm_device_get_state (best); + + /* There's only a best activating device if the best device + * among all activating and already-activated devices is a + * still-activating one. + */ + if (state >= NM_DEVICE_STATE_SECONDARIES) + return NULL; + } + return best; } static NMDevice * -get_best_ip6_device (NMManager *manager) +get_best_ip6_device (NMManager *manager, gboolean fully_activated) { GSList *devices, *iter; NMDevice *best = NULL; @@ -161,6 +191,7 @@ get_best_ip6_device (NMManager *manager) for (iter = devices; iter; iter = g_slist_next (iter)) { NMDevice *dev = NM_DEVICE (iter->data); NMDeviceType devtype = nm_device_get_device_type (dev); + NMDeviceState state = nm_device_get_state (dev); NMActRequest *req; NMConnection *connection; NMIP6Config *ip6_config; @@ -168,12 +199,21 @@ get_best_ip6_device (NMManager *manager) int prio; const char *method = NULL; - if ( nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED - && nm_device_get_state (dev) != NM_DEVICE_STATE_SECONDARIES) + if ( state <= NM_DEVICE_STATE_DISCONNECTED + || state >= NM_DEVICE_STATE_DEACTIVATING) + continue; + + if (fully_activated && state < NM_DEVICE_STATE_SECONDARIES) continue; ip6_config = nm_device_get_ip6_config (dev); - if (!ip6_config) + if (ip6_config) { + if (!nm_ip6_config_get_gateway (ip6_config) && (devtype != NM_DEVICE_TYPE_MODEM)) + continue; + + if (nm_ip6_config_get_never_default (ip6_config)) + continue; + } else if (fully_activated) continue; req = nm_device_get_act_request (dev); @@ -181,20 +221,15 @@ get_best_ip6_device (NMManager *manager) connection = nm_act_request_get_connection (req); g_assert (connection); - /* Never set the default route through an IPv4LL-addressed device */ s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (s_ip6) + if (s_ip6) { method = nm_setting_ip6_config_get_method (s_ip6); + if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) + continue; - if (method && !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) - continue; - - if (!nm_ip6_config_get_gateway (ip6_config) && (devtype != NM_DEVICE_TYPE_MODEM)) - continue; - - /* 'never-default' devices can't ever be the default */ - if (s_ip6 && nm_setting_ip6_config_get_never_default (s_ip6)) - continue; + if (nm_setting_ip6_config_get_never_default (s_ip6)) + continue; + } prio = nm_device_get_priority (dev); if (prio > 0 && prio < best_prio) { @@ -203,6 +238,20 @@ get_best_ip6_device (NMManager *manager) } } + if (!best) + return NULL; + + if (!fully_activated) { + NMDeviceState state = nm_device_get_state (best); + + /* There's only a best activating device if the best device + * among all activating and already-activated devices is an + * activating one. + */ + if (state >= NM_DEVICE_STATE_SECONDARIES) + return NULL; + } + return best; } @@ -346,9 +395,9 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6) /* Try automatically determined hostname from the best device's IP config */ if (!best4) - best4 = get_best_ip4_device (priv->manager); + best4 = get_best_ip4_device (priv->manager, TRUE); if (!best6) - best6 = get_best_ip6_device (priv->manager); + best6 = get_best_ip6_device (priv->manager, TRUE); if (!best4 && !best6) { /* No best device; fall back to original hostname or if there wasn't @@ -543,7 +592,7 @@ get_best_ip4_config (NMPolicy *policy, /* If no VPN connections, we use the best device instead */ if (!ip4_config) { - device = get_best_ip4_device (priv->manager); + device = get_best_ip4_device (priv->manager, TRUE); if (device) { ip4_config = nm_device_get_ip4_config (device); g_assert (ip4_config); @@ -602,7 +651,13 @@ update_ip4_routing (NMPolicy *policy, gboolean force_update) */ ip4_config = get_best_ip4_config (policy, FALSE, &ip_iface, &ip_ifindex, &best_ac, &best, &vpn); if (!ip4_config) { + gboolean changed; + + changed = (priv->default_device4 != NULL); priv->default_device4 = NULL; + if (changed) + g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP4_DEVICE); + return; } g_assert ((best || vpn) && best_ac); @@ -646,6 +701,7 @@ update_ip4_routing (NMPolicy *policy, gboolean force_update) connection = nm_active_connection_get_connection (best_ac); nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for IPv4 routing and DNS.", nm_connection_get_id (connection), ip_iface); + g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP4_DEVICE); } static NMIP6Config * @@ -714,7 +770,7 @@ get_best_ip6_config (NMPolicy *policy, /* If no VPN connections, we use the best device instead */ if (!ip6_config) { - device = get_best_ip6_device (priv->manager); + device = get_best_ip6_device (priv->manager, TRUE); if (device) { req = nm_device_get_act_request (device); g_assert (req); @@ -773,7 +829,13 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update) */ ip6_config = get_best_ip6_config (policy, FALSE, &ip_iface, &ip_ifindex, &best_ac, &best, &vpn); if (!ip6_config) { + gboolean changed; + + changed = (priv->default_device6 != NULL); priv->default_device6 = NULL; + if (changed) + g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP6_DEVICE); + return; } g_assert ((best || vpn) && best_ac); @@ -827,6 +889,7 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update) connection = nm_active_connection_get_connection (best_ac); nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for IPv6 routing and DNS.", nm_connection_get_id (connection), ip_iface); + g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP6_DEVICE); } static void @@ -851,6 +914,30 @@ update_routing_and_dns (NMPolicy *policy, gboolean force_update) g_object_unref (mgr); } +static void +check_activating_devices (NMPolicy *policy) +{ + NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy); + GObject *object = G_OBJECT (policy); + NMDevice *best4, *best6 = NULL; + + best4 = get_best_ip4_device (priv->manager, FALSE); + best6 = get_best_ip6_device (priv->manager, FALSE); + + g_object_freeze_notify (object); + + if (best4 != priv->activating_device4) { + priv->activating_device4 = best4; + g_object_notify (object, NM_POLICY_ACTIVATING_IP4_DEVICE); + } + if (best6 != priv->activating_device6) { + priv->activating_device6 = best6; + g_object_notify (object, NM_POLICY_ACTIVATING_IP6_DEVICE); + } + + g_object_thaw_notify (object); +} + static void set_connection_auto_retries (NMConnection *connection, guint retries) { @@ -1420,6 +1507,8 @@ device_state_changed (NMDevice *device, default: break; } + + check_activating_devices (policy); } static void @@ -1988,11 +2077,61 @@ nm_policy_new (NMManager *manager, NMSettings *settings) return policy; } +NMDevice * +nm_policy_get_default_ip4_device (NMPolicy *policy) +{ + return NM_POLICY_GET_PRIVATE (policy)->default_device4; +} + +NMDevice * +nm_policy_get_default_ip6_device (NMPolicy *policy) +{ + return NM_POLICY_GET_PRIVATE (policy)->default_device6; +} + +NMDevice * +nm_policy_get_activating_ip4_device (NMPolicy *policy) +{ + return NM_POLICY_GET_PRIVATE (policy)->activating_device4; +} + +NMDevice * +nm_policy_get_activating_ip6_device (NMPolicy *policy) +{ + return NM_POLICY_GET_PRIVATE (policy)->activating_device6; +} + static void nm_policy_init (NMPolicy *policy) { } +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMPolicy *policy = NM_POLICY (object); + NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy); + + switch (prop_id) { + case PROP_DEFAULT_IP4_DEVICE: + g_value_set_object (value, priv->default_device4); + break; + case PROP_DEFAULT_IP6_DEVICE: + g_value_set_object (value, priv->default_device6); + break; + case PROP_ACTIVATING_IP4_DEVICE: + g_value_set_object (value, priv->activating_device4); + break; + case PROP_ACTIVATING_IP6_DEVICE: + g_value_set_object (value, priv->activating_device6); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static void dispose (GObject *object) { @@ -2067,5 +2206,35 @@ nm_policy_class_init (NMPolicyClass *policy_class) g_type_class_add_private (policy_class, sizeof (NMPolicyPrivate)); + object_class->get_property = get_property; object_class->dispose = dispose; + + g_object_class_install_property + (object_class, PROP_DEFAULT_IP4_DEVICE, + g_param_spec_object (NM_POLICY_DEFAULT_IP4_DEVICE, + "Default IP4 device", + "Default IP4 device", + NM_TYPE_DEVICE, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_DEFAULT_IP6_DEVICE, + g_param_spec_object (NM_POLICY_DEFAULT_IP6_DEVICE, + "Default IP6 device", + "Default IP6 device", + NM_TYPE_DEVICE, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_ACTIVATING_IP4_DEVICE, + g_param_spec_object (NM_POLICY_ACTIVATING_IP4_DEVICE, + "Activating default IP4 device", + "Activating default IP4 device", + NM_TYPE_DEVICE, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_ACTIVATING_IP6_DEVICE, + g_param_spec_object (NM_POLICY_ACTIVATING_IP6_DEVICE, + "Activating default IP6 device", + "Activating default IP6 device", + NM_TYPE_DEVICE, + G_PARAM_READABLE)); } diff --git a/src/nm-policy.h b/src/nm-policy.h index 6eafc2e8b5..6e83182a90 100644 --- a/src/nm-policy.h +++ b/src/nm-policy.h @@ -32,6 +32,11 @@ #define NM_IS_POLICY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_POLICY)) #define NM_POLICY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_POLICY, NMPolicyClass)) +#define NM_POLICY_DEFAULT_IP4_DEVICE "default-ip4-device" +#define NM_POLICY_DEFAULT_IP6_DEVICE "default-ip6-device" +#define NM_POLICY_ACTIVATING_IP4_DEVICE "activating-ip4-device" +#define NM_POLICY_ACTIVATING_IP6_DEVICE "activating-ip6-device" + typedef struct { GObject parent; } NMPolicy; @@ -45,4 +50,9 @@ GType nm_policy_get_type (void); NMPolicy *nm_policy_new (NMManager *manager, NMSettings *settings); +NMDevice *nm_policy_get_default_ip4_device (NMPolicy *policy); +NMDevice *nm_policy_get_default_ip6_device (NMPolicy *policy); +NMDevice *nm_policy_get_activating_ip4_device (NMPolicy *policy); +NMDevice *nm_policy_get_activating_ip6_device (NMPolicy *policy); + #endif /* NM_POLICY_H */