2008-10-10 Dan Williams <dcbw@redhat.com>
authorDan Williams <dcbw@redhat.com>
Fri, 10 Oct 2008 23:05:45 +0000 (23:05 +0000)
committerDan Williams <dcbw@redhat.com>
Fri, 10 Oct 2008 23:05:45 +0000 (23:05 +0000)
Rework default route handling to consolidate decisions in the policy,
and to take active VPN connections into account when changing the default
route (bgo #545912)

* src/NetworkManager.c
- (main): pass the vpn_manager to the policy so it knows about active
VPN connections; clean up the named manager which wasn't done before

* src/NetworkManagerPolicy.c
  src/NetworkManagerPolicy.h
- (nm_policy_new): get a clue about the vpn_manager
- (update_default_route): remove, fold into update_routing_and_dns()
- (update_routing_and_dns): handle active VPN connections too; an
active VPN connection becomes the default route if it does not have
server-specified or user-specified custom routes.  Otherwise, the
best active device gets the default route
- (vpn_connection_activated, vpn_connection_deactivated, nm_policy_new,
   nm_policy_destroy): track VPN connection activation and deactivation
and update the default route when appropriate

* src/NetworkManagerSystem.c
  src/NetworkManagerSystem.h
- (nm_system_vpn_device_unset_from_ip4_config): remove, put functionality
in the VPN connection itself
- (nm_system_vpn_device_set_from_ip4_config,
   nm_system_device_set_from_ip4_config): merge together to make
nm_system_apply_ip4_config()
- (add_vpn_gateway_route): add a route to the VPN's external gateway
via the parent device
- (nm_system_apply_ip4_config): simplify
- (add_ip4_route_to_gateway): new function; add a direct route to the
gateway if needed
- (nm_system_device_replace_default_ip4_route): simplify, break gateway
route stuff out into add_ip4_route_to_gateway() for clarity

* src/nm-device.c
- (nm_device_set_ip4_config): update for nm_system_apply_ip4_config()

* src/vpn-manager/nm-vpn-connection.c
  src/vpn-manager/nm-vpn-connection.h
- (nm_vpn_connection_get_ip4_config, nm_vpn_connection_get_ip_iface,
   nm_vpn_connection_get_parent_device): add
- (nm_vpn_connection_ip4_config_get): make the requirement of a tunnel
device explicit
- (connection_state_changed): update the named manager now that
nm_system_vpn_device_unset_from_ip4_config() is gone; do something
useful on errors

* src/vpn-manager/nm-vpn-manager.c
  src/vpn-manager/nm-vpn-manager.h
- Add a 'connection-activated' signal
- (nm_vpn_manager_get_active_connections): new function; mainly for the
policy to find out about active VPN connections

git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@4167 4912f4e0-d625-0410-9fb7-b9a5a253dbdc

ChangeLog
src/NetworkManager.c
src/NetworkManagerPolicy.c
src/NetworkManagerPolicy.h
src/NetworkManagerSystem.c
src/NetworkManagerSystem.h
src/nm-device.c
src/vpn-manager/nm-vpn-connection.c
src/vpn-manager/nm-vpn-connection.h
src/vpn-manager/nm-vpn-manager.c
src/vpn-manager/nm-vpn-manager.h

index 5c580b8..4d08a21 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,59 @@
+2008-10-10  Dan Williams  <dcbw@redhat.com>
+
+       Rework default route handling to consolidate decisions in the policy,
+       and to take active VPN connections into account when changing the default
+       route (bgo #545912)
+
+       * src/NetworkManager.c
+               - (main): pass the vpn_manager to the policy so it knows about active
+                       VPN connections; clean up the named manager which wasn't done before
+
+       * src/NetworkManagerPolicy.c
+         src/NetworkManagerPolicy.h
+               - (nm_policy_new): get a clue about the vpn_manager
+               - (update_default_route): remove, fold into update_routing_and_dns()
+               - (update_routing_and_dns): handle active VPN connections too; an
+                       active VPN connection becomes the default route if it does not have
+                       server-specified or user-specified custom routes.  Otherwise, the
+                       best active device gets the default route
+               - (vpn_connection_activated, vpn_connection_deactivated, nm_policy_new,
+                  nm_policy_destroy): track VPN connection activation and deactivation
+                       and update the default route when appropriate
+
+       * src/NetworkManagerSystem.c
+         src/NetworkManagerSystem.h
+               - (nm_system_vpn_device_unset_from_ip4_config): remove, put functionality
+                       in the VPN connection itself
+               - (nm_system_vpn_device_set_from_ip4_config,
+                  nm_system_device_set_from_ip4_config): merge together to make
+                       nm_system_apply_ip4_config()
+               - (add_vpn_gateway_route): add a route to the VPN's external gateway
+                       via the parent device
+               - (nm_system_apply_ip4_config): simplify
+               - (add_ip4_route_to_gateway): new function; add a direct route to the
+                       gateway if needed
+               - (nm_system_device_replace_default_ip4_route): simplify, break gateway
+                       route stuff out into add_ip4_route_to_gateway() for clarity
+
+       * src/nm-device.c
+               - (nm_device_set_ip4_config): update for nm_system_apply_ip4_config()
+
+       * src/vpn-manager/nm-vpn-connection.c
+         src/vpn-manager/nm-vpn-connection.h
+               - (nm_vpn_connection_get_ip4_config, nm_vpn_connection_get_ip_iface,
+                  nm_vpn_connection_get_parent_device): add
+               - (nm_vpn_connection_ip4_config_get): make the requirement of a tunnel
+                       device explicit
+               - (connection_state_changed): update the named manager now that
+                       nm_system_vpn_device_unset_from_ip4_config() is gone; do something
+                       useful on errors
+
+       * src/vpn-manager/nm-vpn-manager.c
+         src/vpn-manager/nm-vpn-manager.h
+               - Add a 'connection-activated' signal
+               - (nm_vpn_manager_get_active_connections): new function; mainly for the
+                       policy to find out about active VPN connections
+
 2008-10-10  Tambet Ingo  <tambet@gmail.com>
 
        * src/nm-logging.c (nm_logging_setup): Don't use LOG_CONS when running as
index e3079db..948fb66 100644 (file)
@@ -308,7 +308,7 @@ main (int argc, char *argv[])
                goto done;
        }
 
-       policy = nm_policy_new (manager);
+       policy = nm_policy_new (manager, vpn_manager);
        if (policy == NULL) {
                nm_error ("Failed to initialize the policy.");
                goto done;
@@ -352,6 +352,9 @@ done:
        if (vpn_manager)
                g_object_unref (vpn_manager);
 
+       if (named_mgr)
+               g_object_unref (named_mgr);
+
        if (sup_mgr)
                g_object_unref (sup_mgr);
 
index 25024c2..46c51e9 100644 (file)
@@ -43,6 +43,7 @@
 #include "nm-setting-connection.h"
 #include "NetworkManagerSystem.h"
 #include "nm-named-manager.h"
+#include "nm-vpn-manager.h"
 
 typedef struct LookupThread LookupThread;
 
@@ -69,6 +70,10 @@ struct NMPolicy {
        GSList *signal_ids;
        GSList *dev_signal_ids;
 
+       NMVPNManager *vpn_manager;
+       gulong vpn_activated_id;
+       gulong vpn_deactivated_id;
+
        NMDevice *default_device;
 
        LookupThread *lookup;
@@ -461,35 +466,22 @@ update_system_hostname (NMPolicy *policy, NMDevice *best)
        }
 }
 
-static void
-update_default_route (NMPolicy *policy, NMDevice *new)
-{
-       const char *ip_iface;
-
-       /* FIXME: Not sure if the following makes any sense. */
-       /* If iface and ip_iface are the same, it's a regular network device and we
-          treat it as such. However, if they differ, it's most likely something like
-          a serial device with ppp interface, so route all the traffic to it. */
-       ip_iface = nm_device_get_ip_iface (new);
-       if (strcmp (ip_iface, nm_device_get_iface (new))) {
-               nm_system_device_replace_default_ip4_route (ip_iface, 0, 0);
-       } else {
-               NMIP4Config *config;
-               const NMSettingIP4Address *def_addr;
-
-               config = nm_device_get_ip4_config (new);
-               def_addr = nm_ip4_config_get_address (config, 0);
-               nm_system_device_replace_default_ip4_route (ip_iface, def_addr->gateway, nm_ip4_config_get_mss (config));
-       }
-}
-
 static void
 update_routing_and_dns (NMPolicy *policy, gboolean force_update)
 {
+       NMNamedIPConfigType dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE;
        NMDevice *best = NULL;
        NMActRequest *best_req = NULL;
        NMNamedManager *named_mgr;
-       GSList *devices = NULL, *iter;
+       GSList *devices = NULL, *iter, *vpns;
+       NMIP4Config *ip4_config = NULL;
+       const char *ip_iface = NULL;
+       const char *parent_iface = NULL;
+       NMVPNConnection *vpn = NULL;
+       NMConnection *connection = NULL;
+       NMSettingConnection *s_con = NULL;
+       guint32 parent_mss = 0;
+       guint32 gateway = 0;
 
        best = get_best_device (policy->manager, &best_req);
        if (!best)
@@ -497,8 +489,69 @@ update_routing_and_dns (NMPolicy *policy, gboolean force_update)
        if (!force_update && (best == policy->default_device))
                goto out;
 
-       update_default_route (policy, best);
-       
+       /* If a VPN connection is active, it is preferred */
+       vpns = nm_vpn_manager_get_active_connections (policy->vpn_manager);
+       for (iter = vpns; iter; iter = g_slist_next (iter)) {
+               NMVPNConnection *candidate = NM_VPN_CONNECTION (iter->data);
+
+               if (!vpn && (nm_vpn_connection_get_vpn_state (candidate) == NM_VPN_CONNECTION_STATE_ACTIVATED))
+                       vpn = g_object_ref (candidate);
+               g_object_unref (candidate);
+       }
+       g_slist_free (vpns);
+
+       /* VPNs are the default route only if they don't have custom routes */
+       if (vpn) {
+               NMIP4Config *vpn_config;
+
+               vpn_config = nm_vpn_connection_get_ip4_config (vpn);
+               if (nm_ip4_config_get_num_routes (vpn_config) == 0) {
+                       NMIP4Config *parent_ip4;
+                       NMDevice *parent;
+
+                       connection = nm_vpn_connection_get_connection (vpn);
+                       ip_iface = nm_vpn_connection_get_ip_iface (vpn);
+                       ip4_config = vpn_config;
+
+                       parent = nm_vpn_connection_get_parent_device (vpn);
+                       parent_iface = nm_device_get_ip_iface (parent);
+                       parent_ip4 = nm_device_get_ip4_config (parent);
+                       if (parent_ip4)
+                               parent_mss = nm_ip4_config_get_mss (parent_ip4);
+
+                       dns_type = NM_NAMED_IP_CONFIG_TYPE_VPN;
+               }
+               g_object_unref (vpn);
+       }
+
+       /* The best device gets the default route if a VPN connection didn't */
+       if (!ip_iface || !ip4_config) {
+               const NMSettingIP4Address *addr;
+
+               connection = nm_act_request_get_connection (best_req);
+               ip_iface = nm_device_get_ip_iface (best);
+               ip4_config = nm_device_get_ip4_config (best);
+               if (ip4_config) {
+                       addr = nm_ip4_config_get_address (ip4_config, 0);
+                       gateway = addr->gateway;
+               }
+
+               dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE;
+       }
+
+       if (!ip_iface || !ip4_config) {
+               nm_warning ("%s: couldn't determine IP interface (%p) or IPv4 config (%p)!",
+                           __func__, ip_iface, ip4_config);
+               goto out;
+       }
+
+       /* Set the new default route */
+       nm_system_device_replace_default_ip4_route (ip_iface,
+                                                   gateway,
+                                                   nm_ip4_config_get_mss (ip4_config),
+                                                   parent_iface,
+                                                   parent_mss);
+
        /* Update the default active connection.  Only mark the new default
         * active connection after setting default = FALSE on all other connections
         * first.  The order is important, we don't want two connections marked
@@ -515,10 +568,7 @@ update_routing_and_dns (NMPolicy *policy, gboolean force_update)
        }
 
        named_mgr = nm_named_manager_get ();
-       nm_named_manager_add_ip4_config (named_mgr,
-                                                                        nm_device_get_ip_iface (best),
-                                                                        nm_device_get_ip4_config (best),
-                                                                        NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE);
+       nm_named_manager_add_ip4_config (named_mgr, ip_iface, ip4_config, dns_type);
        g_object_unref (named_mgr);
 
        /* Now set new default active connection _after_ updating DNS info, so that
@@ -527,8 +577,13 @@ update_routing_and_dns (NMPolicy *policy, gboolean force_update)
        if (best_req)
                nm_act_request_set_default (best_req, TRUE);
 
-       nm_info ("Policy set (%s) as default device for routing and DNS.",
-                nm_device_get_iface (best));
+       if (connection)
+               s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+
+       if (s_con && s_con->id)
+               nm_info ("Policy set '%s' (%s) as default for routing and DNS.", s_con->id, ip_iface);
+       else
+               nm_info ("Policy set (%s) as default for routing and DNS.", ip_iface);
 
 out:
        /* Update the system hostname */
@@ -617,6 +672,24 @@ auto_activate_device (gpointer user_data)
 
 /*****************************************************************************/
 
+static void
+vpn_connection_activated (NMVPNManager *manager,
+                          NMVPNConnection *vpn,
+                          gpointer user_data)
+{
+       update_routing_and_dns ((NMPolicy *) user_data, TRUE);
+}
+
+static void
+vpn_connection_deactivated (NMVPNManager *manager,
+                            NMVPNConnection *vpn,
+                            NMVPNConnectionState state,
+                            NMVPNConnectionStateReason reason,
+                            gpointer user_data)
+{
+       update_routing_and_dns ((NMPolicy *) user_data, TRUE);
+}
+
 static void
 global_state_changed (NMManager *manager, NMState state, gpointer user_data)
 {
@@ -868,7 +941,7 @@ connection_removed (NMManager *manager,
 }
 
 NMPolicy *
-nm_policy_new (NMManager *manager)
+nm_policy_new (NMManager *manager, NMVPNManager *vpn_manager)
 {
        NMPolicy *policy;
        static gboolean initialized = FALSE;
@@ -881,6 +954,14 @@ nm_policy_new (NMManager *manager)
        policy->manager = g_object_ref (manager);
        policy->update_state_id = 0;
 
+       policy->vpn_manager = g_object_ref (vpn_manager);
+       id = g_signal_connect (policy->vpn_manager, "connection-activated",
+                              G_CALLBACK (vpn_connection_activated), policy);
+       policy->vpn_activated_id = id;
+       id = g_signal_connect (policy->vpn_manager, "connection-deactivated",
+                              G_CALLBACK (vpn_connection_deactivated), policy);
+       policy->vpn_deactivated_id = id;
+
        id = g_signal_connect (manager, "state-changed",
                               G_CALLBACK (global_state_changed), policy);
        policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
@@ -944,6 +1025,9 @@ nm_policy_destroy (NMPolicy *policy)
        }
        g_slist_free (policy->pending_activation_checks);
 
+       g_signal_handler_disconnect (policy->vpn_manager, policy->vpn_activated_id);
+       g_signal_handler_disconnect (policy->vpn_manager, policy->vpn_deactivated_id);
+
        for (iter = policy->signal_ids; iter; iter = g_slist_next (iter))
                g_signal_handler_disconnect (policy->manager, (gulong) iter->data);
        g_slist_free (policy->signal_ids);
index 9f2c523..5ab379f 100644 (file)
 
 #include "NetworkManager.h"
 #include "nm-manager.h"
+#include "nm-vpn-manager.h"
 #include "nm-device.h"
 #include "nm-activation-request.h"
 
 typedef struct NMPolicy NMPolicy;
 
-NMPolicy *nm_policy_new (NMManager *manager);
+NMPolicy *nm_policy_new (NMManager *manager, NMVPNManager *vpn_manager);
 void nm_policy_destroy (NMPolicy *policy);
 
 #endif /* NETWORK_MANAGER_POLICY_H */
index 8f836d9..653e90a 100644 (file)
@@ -273,114 +273,75 @@ add_ip4_addresses (NMIP4Config *config, const char *iface)
        return TRUE;
 }
 
-/*
- * nm_system_device_set_from_ip4_config
- *
- * Set IPv4 configuration of the device from an NMIP4Config object.
- *
- */
-gboolean
-nm_system_device_set_from_ip4_config (const char *iface,
-                                                          NMIP4Config *config,
-                                                          int priority)
+static void
+add_vpn_gateway_route (NMDevice *device, const char *iface, NMIP4Config *config)
 {
-       int len, i;
+       NMIP4Config *ad_config;
+       guint32 ad_gw = 0, vpn_gw = 0, i;
+       const NMSettingIP4Address *tmp;
 
-       g_return_val_if_fail (iface != NULL, FALSE);
-       g_return_val_if_fail (config != NULL, FALSE);
+       g_return_if_fail (NM_IS_DEVICE (device));
 
-       if (!add_ip4_addresses (config, iface))
-               return FALSE;
-
-       sleep (1);
+       ad_config = nm_device_get_ip4_config (device);
+       g_return_if_fail (ad_config != NULL);
 
-       len = nm_ip4_config_get_num_routes (config);
-       for (i = 0; i < len; i++) {
-               const NMSettingIP4Route *route = nm_ip4_config_get_route (config, i);
-
-               nm_system_device_set_ip4_route (iface, config, 
-                                                                 route->address,
-                                                                 route->prefix,
-                                                                 route->next_hop,
-                                                                 route->metric,
-                                                                 nm_ip4_config_get_mss (config));
+       /* Set up a route to the VPN gateway's public IP address through the default
+        * network device.
+        */
+       for (i = 0; i < nm_ip4_config_get_num_addresses (ad_config); i++) {
+               tmp = nm_ip4_config_get_address (ad_config, i);
+               if (tmp->gateway) {
+                       ad_gw = tmp->gateway;
+                       break;
+               }
        }
 
-       if (nm_ip4_config_get_mtu (config))
-               nm_system_device_set_mtu (iface, nm_ip4_config_get_mtu (config));
+       if (!ad_gw)
+               return;
 
-       if (priority > 0)
-               nm_system_device_set_priority (iface, config, priority);
+       for (i = 0; i < nm_ip4_config_get_num_addresses (config); i++) {
+               tmp = nm_ip4_config_get_address (config, i);
+               if (tmp->gateway) {
+                       vpn_gw = tmp->gateway;
+                       break;
+               }
+       }
 
-       return TRUE;
+       nm_system_device_set_ip4_route (nm_device_get_ip_iface (device),
+                                       ad_config, vpn_gw, 32, ad_gw, 0,
+                                       nm_ip4_config_get_mss (ad_config));
 }
 
 /*
- * nm_system_vpn_device_set_from_ip4_config
+ * nm_system_apply_ip4_config
  *
- * Set IPv4 configuration of a VPN device from an NMIP4Config object.
+ * Set IPv4 configuration of the device from an NMIP4Config object.
  *
  */
 gboolean
-nm_system_vpn_device_set_from_ip4_config (NMDevice *active_device,
-                                          const char *iface,
-                                          NMIP4Config *config)
+nm_system_apply_ip4_config (NMDevice *device,
+                            const char *iface,
+                            NMIP4Config *config,
+                            int priority,
+                            gboolean is_vpn)
 {
-       NMIP4Config *ad_config = NULL;
-       NMNamedManager *named_mgr;
-       int num;
        int i;
 
+       g_return_val_if_fail (iface != NULL, FALSE);
        g_return_val_if_fail (config != NULL, FALSE);
 
-       /* Set up a route to the VPN gateway through the real network device */
-       if (active_device && (ad_config = nm_device_get_ip4_config (active_device))) {
-               guint32 ad_gw = 0, vpn_gw = 0;
-               const NMSettingIP4Address *tmp;
-
-               num = nm_ip4_config_get_num_addresses (ad_config);
-               for (i = 0; i < num; i++) {
-                       tmp = nm_ip4_config_get_address (ad_config, i);
-                       if (tmp->gateway) {
-                               ad_gw = tmp->gateway;
-                               break;
-                       }
-               }
-
-               if (ad_gw) {
-                       num = nm_ip4_config_get_num_addresses (config);
-                       for (i = 0; i < num; i++) {
-                               tmp = nm_ip4_config_get_address (config, i);
-                               if (tmp->gateway) {
-                                       vpn_gw = tmp->gateway;
-                                       break;
-                               }
-                       }
-
-                       nm_system_device_set_ip4_route (nm_device_get_ip_iface (active_device),
-                                                                         ad_config, vpn_gw, 32, ad_gw, 0,
-                                                                         nm_ip4_config_get_mss (config));
-               }
-       }
-
-       if (!iface || !strlen (iface))
-               goto out;
-
-       nm_system_device_set_up_down_with_iface (iface, TRUE, NULL);
-
        if (!add_ip4_addresses (config, iface))
-               goto out;
+               return FALSE;
 
-       /* Set the MTU */
-       if (nm_ip4_config_get_mtu (config))
-               nm_system_device_set_mtu (iface, nm_ip4_config_get_mtu (config));
+       if (is_vpn)
+               add_vpn_gateway_route (device, iface, config);
 
-       /* Set routes */
-       num = nm_ip4_config_get_num_routes (config);
-       for (i = 0; i < num; i++) {
+       sleep (1);
+
+       for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) {
                const NMSettingIP4Route *route = nm_ip4_config_get_route (config, i);
 
-               nm_system_device_set_ip4_route (iface, config,
+               nm_system_device_set_ip4_route (iface, config, 
                                                                  route->address,
                                                                  route->prefix,
                                                                  route->next_hop,
@@ -388,39 +349,15 @@ nm_system_vpn_device_set_from_ip4_config (NMDevice *active_device,
                                                                  nm_ip4_config_get_mss (config));
        }
 
-       if (num == 0)
-               nm_system_device_replace_default_ip4_route (iface, 0, 0);
-
-out:
-       named_mgr = nm_named_manager_get ();
-       nm_named_manager_add_ip4_config (named_mgr, iface, config, NM_NAMED_IP_CONFIG_TYPE_VPN);
-       g_object_unref (named_mgr);
-
-       return TRUE;
-}
-
-
-/*
- * nm_system_vpn_device_unset_from_ip4_config
- *
- * Unset an IPv4 configuration of a VPN device from an NMIP4Config object.
- *
- */
-gboolean nm_system_vpn_device_unset_from_ip4_config (NMDevice *active_device, const char *iface, NMIP4Config *config)
-{
-       NMNamedManager *named_mgr;
-
-       g_return_val_if_fail (active_device != NULL, FALSE);
-       g_return_val_if_fail (config != NULL, FALSE);
+       if (nm_ip4_config_get_mtu (config))
+               nm_system_device_set_mtu (iface, nm_ip4_config_get_mtu (config));
 
-       named_mgr = nm_named_manager_get ();
-       nm_named_manager_remove_ip4_config (named_mgr, iface, config);
-       g_object_unref (named_mgr);
+       if (priority > 0)
+               nm_system_device_set_priority (iface, config, priority);
 
        return TRUE;
 }
 
-
 /*
  * nm_system_device_set_up_down
  *
@@ -547,6 +484,55 @@ nm_system_device_set_mtu (const char *iface, guint32 mtu)
        return success;
 }
 
+static struct rtnl_route *
+add_ip4_route_to_gateway (const char *iface, guint32 gw, guint32 mss)
+{
+       struct nl_handle *nlh;
+       struct rtnl_route *route = NULL;
+       struct nl_addr *gw_addr = NULL;
+       int iface_idx, err;
+
+       nlh = nm_netlink_get_default_handle ();
+       g_return_val_if_fail (nlh != NULL, NULL);
+
+       iface_idx = nm_netlink_iface_to_index (iface);
+       if (iface_idx < 0)
+               return NULL;
+
+       /* Gateway might be over a bridge; try adding a route to gateway first */
+       route = rtnl_route_alloc ();
+       if (route == NULL)
+               return NULL;
+
+       rtnl_route_set_oif (route, iface_idx);
+       rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE);
+
+       gw_addr = nl_addr_build (AF_INET, &gw, sizeof (gw));
+       if (!gw_addr)
+               goto error;
+       rtnl_route_set_dst (route, gw_addr);
+       nl_addr_put (gw_addr);
+
+       if (mss) {
+               if (rtnl_route_set_metric (route, RTAX_ADVMSS, mss) < 0)
+                       goto error;
+       }
+
+       /* Add direct route to the gateway */
+       err = rtnl_route_add (nlh, route, 0);
+       if (err) {
+               nm_warning ("Failed to add IPv4 default route on '%s': (%d) %s",
+                           iface, err, nl_geterror ());
+               goto error;
+       }
+
+       return route;
+
+error:
+       rtnl_route_put (route);
+       return NULL;
+}
+
 /*
  * nm_system_replace_default_ip4_route
  *
@@ -554,32 +540,35 @@ nm_system_device_set_mtu (const char *iface, guint32 mtu)
  *
  */
 void
-nm_system_device_replace_default_ip4_route (const char *iface, guint32 gw, guint32 mss)
+nm_system_device_replace_default_ip4_route (const char *iface,
+                                            guint32 gw,
+                                            guint32 mss,
+                                            const char *parent_iface,
+                                            guint32 parent_mss)
 {
-       struct rtnl_route * route;
-       struct rtnl_route * route2 = NULL;
-       struct nl_handle  * nlh;
-       struct nl_addr    * gw_addr;
+       struct rtnl_route *route = NULL;
+       struct rtnl_route *gw_route = NULL;
+       struct nl_handle *nlh;
+       struct nl_addr *gw_addr = NULL;
        int iface_idx, err;
+       gboolean success = FALSE;
 
        nlh = nm_netlink_get_default_handle ();
        g_return_if_fail (nlh != NULL);
 
+       iface_idx = nm_netlink_iface_to_index (iface);
+       if (iface_idx < 0)
+               return;
+
        route = rtnl_route_alloc();
        g_return_if_fail (route != NULL);
 
        rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE);
-
-       iface_idx = nm_netlink_iface_to_index (iface);
-       if (iface_idx < 0)
-               goto out;
        rtnl_route_set_oif (route, iface_idx);
 
-       /* Build up gateway address; a gateway of 0 (used in e.g. PPP links) means
-        * that all packets should be sent to the gateway since it's a point-to-point
-        * link and has no broadcast segment really.
-        */
-       if (!(gw_addr = nl_addr_build (AF_INET, &gw, sizeof (gw))))
+       /* Build up the gateway address */
+       gw_addr = nl_addr_build (AF_INET, &gw, sizeof (gw));
+       if (!gw_addr)
                goto out;
        rtnl_route_set_gateway (route, gw_addr);
        nl_addr_put (gw_addr);
@@ -589,9 +578,11 @@ nm_system_device_replace_default_ip4_route (const char *iface, guint32 gw, guint
                        goto out;
        }
 
+       /* Add the new default route */
        err = rtnl_route_add (nlh, route, NLM_F_REPLACE);
        if (err == 0) {
                /* Everything good */
+               success = TRUE;
                goto out;
        } else if (err != -ESRCH) {
                nm_warning ("rtnl_route_add() returned error %s (%d)\n%s",
@@ -599,41 +590,26 @@ nm_system_device_replace_default_ip4_route (const char *iface, guint32 gw, guint
                goto out;
        }
 
-       /* Gateway might be over a bridge; try adding a route to gateway first */
-       route2 = rtnl_route_alloc ();
-       if (route2 == NULL)
-               goto out;
-       rtnl_route_set_oif (route2, iface_idx);
-       rtnl_route_set_dst (route2, gw_addr);
-
-       if (mss) {
-               if (rtnl_route_set_metric (route2, RTAX_ADVMSS, mss) < 0)
-                       goto out;
-       }
-
-       /* Add route to gateway over bridge */
-       err = rtnl_route_add (nlh, route2, 0);
-       if (err) {
-               nm_warning ("Failed to add IPv4 default route on '%s': %s",
-                                 iface,
-                                 nl_geterror ());
+       /* Try adding a direct route to the gateway first */
+       gw_route = add_ip4_route_to_gateway (parent_iface ? parent_iface : iface,
+                                            gw,
+                                            parent_iface ? parent_mss : mss);
+       if (!gw_route)
                goto out;
-       }
 
-       /* Try adding the route again */
-       err = rtnl_route_add (nlh, route, 0);
-       if (err) {
-               rtnl_route_del (nlh, route2, 0);
-               nm_warning ("Failed to set IPv4 default route on '%s': %s",
-                                 iface,
-                                 nl_geterror ());
+       /* Try adding the original route again */
+       err = rtnl_route_add (nlh, route, NLM_F_REPLACE);
+       if (err != 0) {
+               rtnl_route_del (nlh, gw_route, 0);
+               nm_warning ("Failed to set IPv4 default route on '%s': %s", iface, nl_geterror ());
        }
 
 out:
-       if (route2)
-               rtnl_route_put (route2);
+       if (gw_route)
+               rtnl_route_put (gw_route);
 
-       rtnl_route_put (route);
+       if (route)
+               rtnl_route_put (route);
 }
 
 /*
index 2a1503e..bd8df4a 100644 (file)
@@ -1,5 +1,4 @@
-/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
-
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * Dan Williams <dcbw@redhat.com>
@@ -37,7 +36,9 @@ void                  nm_system_device_flush_ip4_routes_with_iface    (const char *iface);
 
 void                   nm_system_device_replace_default_ip4_route   (const char *iface,
                                                              guint32 gw,
-                                                             guint32 mss);
+                                                             guint32 mss,
+                                                             const char *parent_iface,
+                                                             guint32 parent_mss);
 
 void                   nm_system_device_flush_ip4_addresses                    (NMDevice *dev);
 void                   nm_system_device_flush_ip4_addresses_with_iface (const char *iface);
@@ -45,17 +46,11 @@ void                        nm_system_device_flush_ip4_addresses_with_iface (const char *iface);
 void                   nm_system_enable_loopback                               (void);
 void                   nm_system_update_dns                                    (void);
 
-gboolean               nm_system_device_set_from_ip4_config            (const char *iface,
-                                                                                                NMIP4Config *config,
-                                                                                                int priority);
-
-gboolean               nm_system_vpn_device_set_from_ip4_config        (NMDevice *active_device,
-                                                                        const char *iface,
-                                                                        NMIP4Config *config);
-
-gboolean               nm_system_vpn_device_unset_from_ip4_config      (NMDevice *active_device, 
-                                                                        const char *iface,
-                                                                        NMIP4Config *config);
+gboolean               nm_system_apply_ip4_config              (NMDevice *device,
+                                                         const char *iface,
+                                                         NMIP4Config *config,
+                                                         int priority,
+                                                         gboolean is_vpn);
 
 gboolean               nm_system_device_set_up_down                            (NMDevice *dev,
                                                              gboolean up,
index a7a0fc6..1d5772e 100644 (file)
@@ -1919,7 +1919,7 @@ nm_device_set_ip4_config (NMDevice *self, NMIP4Config *config, NMDeviceStateReas
        if (!nm_ip4_config_get_dbus_path (config))
                nm_ip4_config_export (config);
 
-       success = nm_system_device_set_from_ip4_config (ip_iface, config, nm_device_get_priority (self));
+       success = nm_system_apply_ip4_config (self, ip_iface, config, nm_device_get_priority (self), FALSE);
        if (success)
                nm_device_update_ip4_address (self);
 
index 6307aaa..90c3691 100644 (file)
@@ -46,6 +46,7 @@
 #include "nm-properties-changed-signal.h"
 #include "nm-dbus-glib-types.h"
 #include "NetworkManagerUtils.h"
+#include "nm-named-manager.h"
 
 #include "nm-vpn-connection-glue.h"
 
@@ -354,8 +355,8 @@ print_vpn_config (NMIP4Config *config,
 
 static void
 nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
-                                                   GHashTable *config_hash,
-                                                   gpointer user_data)
+                                  GHashTable *config_hash,
+                                  gpointer user_data)
 {
        NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
        NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
@@ -376,6 +377,14 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
        addr = g_malloc0 (sizeof (NMSettingIP4Address));
        addr->prefix = 24; /* default to class C */
 
+       val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV);
+       if (val)
+               priv->tundev = g_strdup (g_value_get_string (val));
+       else {
+               nm_warning ("%s: invalid or missing tunnel device received!", __func__);
+               goto error;
+       }
+
        val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_GATEWAY);
        if (val)
                addr->gateway = g_value_get_uint (val);
@@ -395,8 +404,9 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
        if (addr->address && addr->prefix) {
                nm_ip4_config_take_address (config, addr);
        } else {
-               g_warning ("%s: invalid IP4 config received!", __func__);
+               nm_warning ("%s: invalid IP4 config received!", __func__);
                g_free (addr);
+               goto error;
        }
 
        val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_DNS);
@@ -423,10 +433,6 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
        if (val)
                nm_ip4_config_set_mtu (config, g_value_get_uint (val));
 
-       val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV);
-       if (val)
-               priv->tundev = g_strdup (g_value_get_string (val));
-
        val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN);
        if (val)
                nm_ip4_config_add_domain (config, g_value_get_string (val));
@@ -452,26 +458,37 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
 
        print_vpn_config (config, priv->tundev, priv->banner);
 
-       priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
-       priv->ip4_config = config;
-
        /* Merge in user overrides from the NMConnection's IPv4 setting */
        s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (priv->connection, NM_TYPE_SETTING_IP4_CONFIG));
        nm_utils_merge_ip4_config (config, s_ip4);
 
-       if (nm_system_vpn_device_set_from_ip4_config (priv->parent_dev, priv->tundev, priv->ip4_config)) {
+       nm_system_device_set_up_down_with_iface (priv->tundev, TRUE, NULL);
+
+       if (nm_system_apply_ip4_config (priv->parent_dev, priv->tundev, config, 0, TRUE)) {
+               NMNamedManager *named_mgr;
+
+               /* Add the VPN to DNS */
+               named_mgr = nm_named_manager_get ();
+               nm_named_manager_add_ip4_config (named_mgr, priv->tundev, config, NM_NAMED_IP_CONFIG_TYPE_VPN);
+               g_object_unref (named_mgr);
+
+               priv->ip4_config = config;
+
                nm_info ("VPN connection '%s' (IP Config Get) complete.",
                            nm_vpn_connection_get_name (connection));
                nm_vpn_connection_set_vpn_state (connection,
                                                 NM_VPN_CONNECTION_STATE_ACTIVATED,
                                                 NM_VPN_CONNECTION_STATE_REASON_NONE);
-       } else {
-               nm_warning ("VPN connection '%s' did not receive valid IP config information.",
-                                 nm_vpn_connection_get_name (connection));
-               nm_vpn_connection_set_vpn_state (connection,
-                                                NM_VPN_CONNECTION_STATE_FAILED,
-                                                NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID);
+               return;
        }
+
+error:
+       nm_warning ("VPN connection '%s' did not receive valid IP config information.",
+                   nm_vpn_connection_get_name (connection));
+       nm_vpn_connection_set_vpn_state (connection,
+                                        NM_VPN_CONNECTION_STATE_FAILED,
+                                        NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID);
+       g_object_unref (config);
 }
 
 static gboolean
@@ -631,6 +648,30 @@ nm_vpn_connection_get_banner (NMVPNConnection *connection)
        return NM_VPN_CONNECTION_GET_PRIVATE (connection)->banner;
 }
 
+NMIP4Config *
+nm_vpn_connection_get_ip4_config (NMVPNConnection *connection)
+{
+       g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+
+       return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip4_config;
+}
+
+const char *
+nm_vpn_connection_get_ip_iface (NMVPNConnection *connection)
+{
+       g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+
+       return NM_VPN_CONNECTION_GET_PRIVATE (connection)->tundev;
+}
+
+NMDevice *
+nm_vpn_connection_get_parent_device (NMVPNConnection *connection)
+{
+       g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+
+       return NM_VPN_CONNECTION_GET_PRIVATE (connection)->parent_dev;
+}
+
 void
 nm_vpn_connection_fail (NMVPNConnection *connection,
                         NMVPNConnectionStateReason reason)
@@ -870,9 +911,12 @@ connection_state_changed (NMVPNConnection *connection,
 
                if (priv->ip4_config) {
                        NMIP4Config *dev_ip4_config;
+                       NMNamedManager *named_mgr;
 
                        /* Remove attributes of the VPN's IP4 Config */
-                       nm_system_vpn_device_unset_from_ip4_config (priv->parent_dev, priv->tundev, priv->ip4_config);
+                       named_mgr = nm_named_manager_get ();
+                       nm_named_manager_remove_ip4_config (named_mgr, priv->tundev, priv->ip4_config);
+                       g_object_unref (named_mgr);
 
                        /* Reset routes, nameservers, and domains of the currently active device */
                        dev_ip4_config = nm_device_get_ip4_config (priv->parent_dev);
index b642023..7007b0b 100644 (file)
@@ -70,5 +70,8 @@ void                 nm_vpn_connection_fail            (NMVPNConnection *connect
                                                         NMVPNConnectionStateReason reason);
 void                 nm_vpn_connection_disconnect      (NMVPNConnection *connection,
                                                         NMVPNConnectionStateReason reason);
+NMIP4Config *        nm_vpn_connection_get_ip4_config  (NMVPNConnection *connection);
+const char *         nm_vpn_connection_get_ip_iface    (NMVPNConnection *connection);
+NMDevice *           nm_vpn_connection_get_parent_device (NMVPNConnection *connection);
 
 #endif /* NM_VPN_CONNECTION_H */
index 26c84e4..e8e47c3 100644 (file)
@@ -20,6 +20,7 @@ typedef struct {
 } NMVPNManagerPrivate;
 
 enum {
+       CONNECTION_ACTIVATED,
        CONNECTION_DEACTIVATED,
 
        LAST_SIGNAL
@@ -128,6 +129,9 @@ connection_vpn_state_changed (NMVPNConnection *connection,
        NMVPNManager *manager = NM_VPN_MANAGER (user_data);
 
        switch (state) {
+       case NM_VPN_CONNECTION_STATE_ACTIVATED:
+               g_signal_emit (manager, signals[CONNECTION_ACTIVATED], 0, connection);
+               break;
        case NM_VPN_CONNECTION_STATE_FAILED:
        case NM_VPN_CONNECTION_STATE_DISCONNECTED:
                g_signal_emit (manager, signals[CONNECTION_DEACTIVATED], 0, connection, state, reason);
@@ -259,6 +263,27 @@ nm_vpn_manager_add_active_connections (NMVPNManager *manager,
        }
 }
 
+GSList *
+nm_vpn_manager_get_active_connections (NMVPNManager *manager)
+{
+       NMVPNManagerPrivate *priv;
+       GSList *iter;
+       GSList *list = NULL;
+
+       g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
+
+       priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
+       for (iter = priv->services; iter; iter = g_slist_next (iter)) {
+               GSList *active, *elt;
+
+               active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
+               for (elt = active; elt; elt = g_slist_next (elt))
+                       list = g_slist_append (list, g_object_ref (NM_VPN_CONNECTION (elt->data)));
+       }
+
+       return list;
+}
+
 NMVPNManager *
 nm_vpn_manager_get (void)
 {
@@ -301,6 +326,14 @@ nm_vpn_manager_class_init (NMVPNManagerClass *manager_class)
        object_class->finalize = finalize;
 
        /* signals */
+       signals[CONNECTION_ACTIVATED] =
+               g_signal_new ("connection-activated",
+                                   G_OBJECT_CLASS_TYPE (object_class),
+                                   G_SIGNAL_RUN_FIRST,
+                                   0, NULL, NULL,
+                                   g_cclosure_marshal_VOID__OBJECT,
+                                   G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
        signals[CONNECTION_DEACTIVATED] =
                g_signal_new ("connection-deactivated",
                                    G_OBJECT_CLASS_TYPE (object_class),
index c4bb605..9d3bf48 100644 (file)
@@ -61,4 +61,6 @@ void nm_vpn_manager_add_active_connections (NMVPNManager *manager,
                                             NMConnection *filter,
                                             GPtrArray *list);
 
+GSList *nm_vpn_manager_get_active_connections (NMVPNManager *manager);
+
 #endif /* NM_VPN_VPN_MANAGER_H */