2008-11-07 Dan Williams <dcbw@redhat.com>
authorDan Williams <dcbw@redhat.com>
Fri, 7 Nov 2008 13:57:39 +0000 (13:57 +0000)
committerDan Williams <dcbw@redhat.com>
Fri, 7 Nov 2008 13:57:39 +0000 (13:57 +0000)
Fix deletion of VPN gateway route on DHCP renew (bgo #558133)

* src/NetworkManagerSystem.c
  src/NetworkManagerSystem.h
- (nm_system_device_set_ip4_route): return the route that was added
- (nm_system_add_ip4_vpn_gateway_route): make add_vpn_gateway_route()
public, clean up, and return the route that was added
- (nm_system_apply_ip4_config): remove VPN related stuff to simplify,
since nm_system_add_ip4_vpn_gateway_route() is now available; add
flags to allow only certain attributes of the NMIP4Config to be
applied

* src/nm-device.c
- (handle_dhcp_lease_change): don't touch the DHCP4 config on failure
- (nm_device_set_ip4_config): use nm_ip4_config_diff() to only apply
what's really changed between the old and new configs; don't export
the new IP4 config on failure; always send the DNS info to the
named manager

* src/vpn-manager/nm-vpn-connection.c
- (device_ip4_config_changed, nm_vpn_connection_new, dispose): track the
parent device's IP4Config and re-add the VPN gateway route when it
changes
- (nm_vpn_connection_ip4_config_get): add the VPN gateway route (since
nm_system_apply_ip4_config() no longer does) and cache it for later
- (connection_state_changed): move cleanup code to its own function
- (vpn_cleanup): delete any previously added VPN gateway route; and
re-apply the parent device's addresses and routes using
nm_system_apply_ip4_config(), not nm_device_set_ip4_config()

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

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

index 4f08683..59bae46 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+2008-11-07  Dan Williams  <dcbw@redhat.com>
+
+       Fix deletion of VPN gateway route on DHCP renew (bgo #558133)
+
+       * src/NetworkManagerSystem.c
+         src/NetworkManagerSystem.h
+               - (nm_system_device_set_ip4_route): return the route that was added
+               - (nm_system_add_ip4_vpn_gateway_route): make add_vpn_gateway_route()
+                       public, clean up, and return the route that was added
+               - (nm_system_apply_ip4_config): remove VPN related stuff to simplify,
+                       since nm_system_add_ip4_vpn_gateway_route() is now available; add
+                       flags to allow only certain attributes of the NMIP4Config to be
+                       applied
+
+       * src/nm-device.c
+               - (handle_dhcp_lease_change): don't touch the DHCP4 config on failure
+               - (nm_device_set_ip4_config): use nm_ip4_config_diff() to only apply
+                       what's really changed between the old and new configs; don't export
+                       the new IP4 config on failure; always send the DNS info to the
+                       named manager
+
+       * src/vpn-manager/nm-vpn-connection.c
+               - (device_ip4_config_changed, nm_vpn_connection_new, dispose): track the
+                       parent device's IP4Config and re-add the VPN gateway route when it
+                       changes
+               - (nm_vpn_connection_ip4_config_get): add the VPN gateway route (since
+                       nm_system_apply_ip4_config() no longer does) and cache it for later
+               - (connection_state_changed): move cleanup code to its own function
+               - (vpn_cleanup): delete any previously added VPN gateway route; and
+                       re-apply the parent device's addresses and routes using
+                       nm_system_apply_ip4_config(), not nm_device_set_ip4_config()
+
 2008-11-07  Dan Williams  <dcbw@redhat.com>
 
        * src/nm-ip4-config.c
index ae75e4e..2181017 100644 (file)
@@ -103,7 +103,7 @@ create_route (int iface_idx, int mss)
        return route;
 }
 
-static void
+static struct rtnl_route *
 nm_system_device_set_ip4_route (const char *iface, 
                                 guint32 ip4_dest,
                                 guint32 ip4_prefix,
@@ -118,17 +118,17 @@ nm_system_device_set_ip4_route (const char *iface,
        int err, iface_idx;
 
        nlh = nm_netlink_get_default_handle ();
-       g_return_if_fail (nlh != NULL);
+       g_return_val_if_fail (nlh != NULL, NULL);
 
        iface_idx = nm_netlink_iface_to_index (iface);
-       g_return_if_fail (iface_idx >= 0);
+       g_return_val_if_fail (iface_idx >= 0, NULL);
 
        route = create_route (iface_idx, mss);
-       g_return_if_fail (route != NULL);
+       g_return_val_if_fail (route != NULL, NULL);
 
        /* Destination */
        dest_addr = nl_addr_build (AF_INET, &ip4_dest, sizeof (ip4_dest));
-       g_return_if_fail (dest_addr != NULL);
+       g_return_val_if_fail (dest_addr != NULL, NULL);
        nl_addr_set_prefixlen (dest_addr, (int) ip4_prefix);
 
        rtnl_route_set_dst (route, dest_addr);
@@ -143,7 +143,7 @@ nm_system_device_set_ip4_route (const char *iface,
                } else {
                        nm_warning ("Invalid gateway");
                        rtnl_route_put (route);
-                       return;
+                       return NULL;
                }
        }
 
@@ -168,17 +168,20 @@ nm_system_device_set_ip4_route (const char *iface,
                                if (err)
                                        rtnl_route_del (nlh, route2, 0);
                        }
-
                        rtnl_route_put (route2);
                }
        }
 
-       if (err)
-               nm_warning ("Failed to set IPv4 route on '%s': %s", iface, nl_geterror ());
-
-       rtnl_route_put (route);
        if (gw_addr)
                nl_addr_put (gw_addr);
+
+       if (err) {
+               nm_warning ("Failed to set IPv4 route on '%s': %s", iface, nl_geterror ());
+               rtnl_route_put (route);
+               route = NULL;
+       }
+
+       return route;
 }
 
 typedef struct {
@@ -267,23 +270,22 @@ add_ip4_addresses (NMIP4Config *config, const char *iface)
        return TRUE;
 }
 
-static void
-add_vpn_gateway_route (NMDevice *parent_device,
-                       const char *iface,
-                       NMIP4Config *vpn_config)
+struct rtnl_route *
+nm_system_add_ip4_vpn_gateway_route (NMDevice *parent_device, NMIP4Config *vpn_config)
 {
        NMIP4Config *parent_config;
        guint32 parent_gw = 0, parent_prefix = 0, vpn_gw = 0, i;
        NMIP4Address *tmp;
+       struct rtnl_route *route = NULL;
 
-       g_return_if_fail (NM_IS_DEVICE (parent_device));
+       g_return_val_if_fail (NM_IS_DEVICE (parent_device), NULL);
 
        /* Set up a route to the VPN gateway's public IP address through the default
         * network device if the VPN gateway is on a different subnet.
         */
 
        parent_config = nm_device_get_ip4_config (parent_device);
-       g_return_if_fail (parent_config != NULL);
+       g_return_val_if_fail (parent_config != NULL, NULL);
 
        for (i = 0; i < nm_ip4_config_get_num_addresses (parent_config); i++) {
                tmp = nm_ip4_config_get_address (parent_config, i);
@@ -303,19 +305,21 @@ add_vpn_gateway_route (NMDevice *parent_device,
        }
 
        if (!parent_gw || !vpn_gw)
-               return;
+               return NULL;
 
        /* If the VPN gateway is in the same subnet as one of the parent device's
         * IP addresses, don't add the host route to it, but a route through the
         * parent device.
         */
        if (ip4_dest_in_same_subnet (parent_config, vpn_gw, parent_prefix)) {
-               nm_system_device_set_ip4_route (nm_device_get_ip_iface (parent_device),
-                                               vpn_gw, 32, 0, 0, nm_ip4_config_get_mss (parent_config));
+               route = nm_system_device_set_ip4_route (nm_device_get_ip_iface (parent_device),
+                                                       vpn_gw, 32, 0, 0, nm_ip4_config_get_mss (parent_config));
        } else {
-               nm_system_device_set_ip4_route (nm_device_get_ip_iface (parent_device),
-                                               vpn_gw, 32, parent_gw, 0, nm_ip4_config_get_mss (parent_config));
+               route = nm_system_device_set_ip4_route (nm_device_get_ip_iface (parent_device),
+                                                       vpn_gw, 32, parent_gw, 0, nm_ip4_config_get_mss (parent_config));
        }
+
+       return route;
 }
 
 /*
@@ -325,46 +329,49 @@ add_vpn_gateway_route (NMDevice *parent_device,
  *
  */
 gboolean
-nm_system_apply_ip4_config (NMDevice *device,
-                            const char *iface,
+nm_system_apply_ip4_config (const char *iface,
                             NMIP4Config *config,
                             int priority,
-                            gboolean is_vpn)
+                            NMIP4ConfigCompareFlags flags)
 {
        int i;
 
        g_return_val_if_fail (iface != NULL, FALSE);
        g_return_val_if_fail (config != NULL, FALSE);
 
-       if (!add_ip4_addresses (config, iface))
-               return FALSE;
-
-       if (is_vpn)
-               add_vpn_gateway_route (device, iface, config);
-
-       sleep (1);
-
-       for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) {
-               NMIP4Route *route = nm_ip4_config_get_route (config, i);
-
-               /* Don't add the route if it's more specific than one of the subnets
-                * the device already has an IP address on.
-                */
-               if (ip4_dest_in_same_subnet (config,
-                                            nm_ip4_route_get_dest (route),
-                                            nm_ip4_route_get_prefix (route)))
-                       continue;
+       if (flags & NM_IP4_COMPARE_FLAG_ADDRESSES) {
+               if (!add_ip4_addresses (config, iface))
+                       return FALSE;
+               sleep (1);
+       }
 
-               nm_system_device_set_ip4_route (iface,
-                                               nm_ip4_route_get_dest (route),
-                                               nm_ip4_route_get_prefix (route),
-                                               nm_ip4_route_get_next_hop (route),
-                                               nm_ip4_route_get_metric (route),
-                                               nm_ip4_config_get_mss (config));
+       if (flags & NM_IP4_COMPARE_FLAG_ROUTES) {
+               for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) {
+                       NMIP4Route *route = nm_ip4_config_get_route (config, i);
+                       struct rtnl_route *tmp;
+
+                       /* Don't add the route if it's more specific than one of the subnets
+                        * the device already has an IP address on.
+                        */
+                       if (ip4_dest_in_same_subnet (config,
+                                                    nm_ip4_route_get_dest (route),
+                                                    nm_ip4_route_get_prefix (route)))
+                               continue;
+
+                       tmp = nm_system_device_set_ip4_route (iface,
+                                                             nm_ip4_route_get_dest (route),
+                                                             nm_ip4_route_get_prefix (route),
+                                                             nm_ip4_route_get_next_hop (route),
+                                                             nm_ip4_route_get_metric (route),
+                                                             nm_ip4_config_get_mss (config));
+                       rtnl_route_put (tmp);
+               }
        }
 
-       if (nm_ip4_config_get_mtu (config))
-               nm_system_device_set_mtu (iface, nm_ip4_config_get_mtu (config));
+       if (flags & NM_IP4_COMPARE_FLAG_MTU) {
+               if (nm_ip4_config_get_mtu (config))
+                       nm_system_device_set_mtu (iface, nm_ip4_config_get_mtu (config));
+       }
 
        if (priority > 0)
                nm_system_device_set_priority (iface, config, priority);
index cfa7ab5..b83a663 100644 (file)
@@ -22,6 +22,9 @@
 #ifndef NETWORK_MANAGER_SYSTEM_H
 #define NETWORK_MANAGER_SYSTEM_H
 
+#include <netlink/route/rtnl.h>
+#include <netlink/route/route.h>
+
 #include <glib.h>
 #include "nm-device.h"
 #include "nm-ip4-config.h"
@@ -44,17 +47,19 @@ gboolean            nm_system_replace_default_ip4_route_vpn (const char *iface,
                                                          const char *parent_iface,
                                                          guint32 parent_mss);
 
+struct rtnl_route *nm_system_add_ip4_vpn_gateway_route (NMDevice *parent_device, NMIP4Config *vpn_config);
+
+
 void                   nm_system_device_flush_ip4_addresses                    (NMDevice *dev);
 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_apply_ip4_config              (NMDevice *device,
-                                                         const char *iface,
+gboolean               nm_system_apply_ip4_config              (const char *iface,
                                                          NMIP4Config *config,
                                                          int priority,
-                                                         gboolean is_vpn);
+                                                         NMIP4ConfigCompareFlags flags);
 
 gboolean               nm_system_device_set_up_down                            (NMDevice *dev,
                                                              gboolean up,
index 3f8e8b3..5cf26d9 100644 (file)
@@ -120,6 +120,8 @@ static void nm_device_take_down (NMDevice *dev, gboolean wait, NMDeviceStateReas
 static gboolean nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware);
 static gboolean nm_device_is_up (NMDevice *self);
 
+static gboolean nm_device_set_ip4_config (NMDevice *dev, NMIP4Config *config, NMDeviceStateReason *reason);
+
 static void
 device_interface_init (NMDeviceInterface *device_interface_class)
 {
@@ -1754,12 +1756,12 @@ handle_dhcp_lease_change (NMDevice *device)
 
        g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, config);
 
-       if (!nm_device_set_ip4_config (device, config, &reason)) {
+       if (nm_device_set_ip4_config (device, config, &reason))
+               nm_dhcp_manager_set_dhcp4_config (priv->dhcp_manager, ip_iface, priv->dhcp4_config);
+       else {
                nm_warning ("Failed to update IP4 config in response to DHCP event.");
                nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
        }
-
-       nm_dhcp_manager_set_dhcp4_config (priv->dhcp_manager, ip_iface, priv->dhcp4_config);
 }
 
 static void
@@ -1905,12 +1907,17 @@ nm_device_get_ip4_config (NMDevice *self)
 }
 
 
-gboolean
-nm_device_set_ip4_config (NMDevice *self, NMIP4Config *config, NMDeviceStateReason *reason)
+static gboolean
+nm_device_set_ip4_config (NMDevice *self,
+                          NMIP4Config *new_config,
+                          NMDeviceStateReason *reason)
 {
        NMDevicePrivate *priv;
        const char *ip_iface;
-       gboolean success;
+       NMIP4Config *old_config = NULL;
+       gboolean success = TRUE;
+       NMIP4ConfigCompareFlags diff = NM_IP4_COMPARE_FLAG_ALL;
+       NMNamedManager *named_mgr;
 
        g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
        g_return_val_if_fail (reason != NULL, FALSE);
@@ -1918,30 +1925,39 @@ nm_device_set_ip4_config (NMDevice *self, NMIP4Config *config, NMDeviceStateReas
        priv = NM_DEVICE_GET_PRIVATE (self);
        ip_iface = nm_device_get_ip_iface (self);
 
-       if (priv->ip4_config) {
-               NMNamedManager *named_mgr;
+       old_config = priv->ip4_config;
 
-               /* Remove any previous IP4 Config from the named manager */
-               named_mgr = nm_named_manager_get ();
-               nm_named_manager_remove_ip4_config (named_mgr, ip_iface, priv->ip4_config);
-               g_object_unref (named_mgr);
+       if (new_config && old_config)
+               diff = nm_ip4_config_diff (new_config, old_config);
 
-               g_object_unref (priv->ip4_config);
+       /* No actual change, do nothing */
+       if (diff == NM_IP4_COMPARE_FLAG_NONE)
+               return TRUE;
+
+       named_mgr = nm_named_manager_get ();
+       if (old_config) {
+               /* Remove any previous IP4 Config from the named manager */
+               nm_named_manager_remove_ip4_config (named_mgr, ip_iface, old_config);
+               g_object_unref (old_config);
                priv->ip4_config = NULL;
        }
 
-       if (!config)
-               return TRUE;
+       if (new_config) {
+               priv->ip4_config = g_object_ref (new_config);
 
-       priv->ip4_config = g_object_ref (config);
+               success = nm_system_apply_ip4_config (ip_iface, new_config, nm_device_get_priority (self), diff);
+               if (success) {
+                       /* Export over D-Bus */
+                       if (!nm_ip4_config_get_dbus_path (new_config))
+                               nm_ip4_config_export (new_config);
 
-       /* Export over D-Bus if needed */
-       if (!nm_ip4_config_get_dbus_path (config))
-               nm_ip4_config_export (config);
+                       /* Add the DNS information to the named manager */
+                       nm_named_manager_add_ip4_config (named_mgr, ip_iface, new_config, NM_NAMED_IP_CONFIG_TYPE_DEFAULT);
 
-       success = nm_system_apply_ip4_config (self, ip_iface, config, nm_device_get_priority (self), FALSE);
-       if (success)
-               nm_device_update_ip4_address (self);
+                       nm_device_update_ip4_address (self);
+               }
+       }
+       g_object_unref (named_mgr);
 
        g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_IP4_CONFIG);
 
index 305af73..f78e1e8 100644 (file)
@@ -139,9 +139,6 @@ void                        nm_device_set_use_dhcp  (NMDevice *dev,
 NMDHCP4Config * nm_device_get_dhcp4_config (NMDevice *dev);
 
 NMIP4Config *  nm_device_get_ip4_config        (NMDevice *dev);
-gboolean               nm_device_set_ip4_config        (NMDevice *dev,
-                                             NMIP4Config *config,
-                                             NMDeviceStateReason *reason);
 
 void *         nm_device_get_system_config_data        (NMDevice *dev);
 
index 389e1bd..8c0a7a9 100644 (file)
@@ -44,6 +44,7 @@
 #include "nm-dbus-glib-types.h"
 #include "NetworkManagerUtils.h"
 #include "nm-named-manager.h"
+#include "nm-netlink.h"
 
 #include "nm-vpn-connection-glue.h"
 
@@ -56,22 +57,25 @@ typedef struct {
        DBusGProxyCall *secrets_call;
 
        NMActRequest *act_request;
-       NMDevice *parent_dev;
        char *ac_path;
 
+       NMDevice *parent_dev;
+       gulong device_monitor;
+       gulong device_ip4;
+
        gboolean is_default;
        NMActiveConnectionState state;
 
        NMVPNConnectionState vpn_state;
        NMVPNConnectionStateReason failure_reason;
-       gulong device_monitor;
        DBusGProxy *proxy;
        guint ipconfig_timeout;
        NMIP4Config *ip4_config;
        guint32 ip4_internal_gw;
        char *tundev;
-       char *tapdev;
        char *banner;
+
+       struct rtnl_route *gw_route;
 } NMVPNConnectionPrivate;
 
 #define NM_VPN_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_CONNECTION, NMVPNConnectionPrivate))
@@ -155,7 +159,7 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
                nm_utils_call_dispatcher ("vpn-up",
                                          priv->connection,
                                          priv->parent_dev,
-                                         priv->tapdev ? priv->tapdev : priv->tundev);
+                                         priv->tundev);
                break;
        case NM_VPN_CONNECTION_STATE_FAILED:
        case NM_VPN_CONNECTION_STATE_DISCONNECTED:
@@ -163,7 +167,7 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
                        nm_utils_call_dispatcher ("vpn-down",
                                                  priv->connection,
                                                  priv->parent_dev,
-                                                 priv->tapdev ? priv->tapdev : priv->tundev);
+                                                 priv->tundev);
                }
                break;
        default:
@@ -193,6 +197,24 @@ device_state_changed (NMDevice *device,
        }
 }
 
+static void
+device_ip4_config_changed (NMDevice *device,
+                           GParamSpec *pspec,
+                           gpointer user_data)
+{
+       NMVPNConnection *vpn = NM_VPN_CONNECTION (user_data);
+       NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (vpn);
+
+       if (priv->vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED)
+               return;
+
+       if (priv->gw_route)
+               rtnl_route_put (priv->gw_route);
+
+       /* Re-add the VPN gateway route */
+       priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev, priv->ip4_config);
+}
+
 NMVPNConnection *
 nm_vpn_connection_new (NMConnection *connection,
                        NMActRequest *act_request,
@@ -218,6 +240,10 @@ nm_vpn_connection_new (NMConnection *connection,
        priv->device_monitor = g_signal_connect (parent_device, "state-changed",
                                                                         G_CALLBACK (device_state_changed),
                                                                         vpn_connection);
+
+       priv->device_ip4 = g_signal_connect (parent_device, "notify::" NM_DEVICE_INTERFACE_IP4_CONFIG,
+                                            G_CALLBACK (device_ip4_config_changed),
+                                            vpn_connection);
        return vpn_connection;
 }
 
@@ -470,9 +496,12 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
 
        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)) {
+       if (nm_system_apply_ip4_config (priv->tundev, config, 0, NM_IP4_COMPARE_FLAG_ALL)) {
                NMNamedManager *named_mgr;
 
+               /* Add any explicit route to the VPN gateway through the parent device */
+               priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev, config);
+
                /* 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);
@@ -889,6 +918,58 @@ call_need_secrets (NMVPNConnection *vpn_connection)
        g_hash_table_destroy (settings);
 }
 
+static void
+vpn_cleanup (NMVPNConnection *connection)
+{
+       NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+       if (priv->tundev) {
+               nm_system_device_set_up_down_with_iface (priv->tundev, FALSE, NULL);
+               nm_system_device_flush_ip4_routes_with_iface (priv->tundev);
+               nm_system_device_flush_ip4_addresses_with_iface (priv->tundev);
+       }
+
+       if (priv->ip4_config) {
+               NMIP4Config *parent_config;
+               NMNamedManager *named_mgr;
+
+               /* Remove attributes of the VPN's 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);
+
+               /* Remove any previously added VPN gateway host route */
+               if (priv->gw_route)
+                       rtnl_route_del (nm_netlink_get_default_handle (), priv->gw_route, 0);
+
+               /* Reset routes and addresses of the currently active device */
+               parent_config = nm_device_get_ip4_config (priv->parent_dev);
+               if (parent_config) {
+                       if (!nm_system_apply_ip4_config (nm_device_get_ip_iface (priv->parent_dev),
+                                                        nm_device_get_ip4_config (priv->parent_dev),
+                                                        nm_device_get_priority (priv->parent_dev),
+                                                        NM_IP4_COMPARE_FLAG_ADDRESSES | NM_IP4_COMPARE_FLAG_ROUTES)) {
+                               nm_warning ("%s: failed to re-apply VPN parent device addresses and routes.", __func__);
+                       }
+               }
+       }
+
+       if (priv->gw_route) {
+               rtnl_route_put (priv->gw_route);
+               priv->gw_route = NULL;
+       }
+
+       if (priv->banner) {
+               g_free (priv->banner);
+               priv->banner = NULL;
+       }
+
+       if (priv->tundev) {
+               g_free (priv->tundev);
+               priv->tundev = NULL;
+       }
+}
+
 static void
 connection_state_changed (NMVPNConnection *connection,
                           NMVPNConnectionState state,
@@ -916,41 +997,7 @@ connection_state_changed (NMVPNConnection *connection,
                        g_object_unref (priv->proxy);
                        priv->proxy = NULL;
                }
-
-               if (priv->tundev) {
-                       nm_system_device_set_up_down_with_iface (priv->tundev, FALSE, NULL);
-                       nm_system_device_flush_ip4_routes_with_iface (priv->tundev);
-                       nm_system_device_flush_ip4_addresses_with_iface (priv->tundev);
-               }
-
-               if (priv->ip4_config) {
-                       NMIP4Config *dev_ip4_config;
-                       NMNamedManager *named_mgr;
-
-                       /* Remove attributes of the VPN's 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);
-                       if (dev_ip4_config) {
-                               NMDeviceStateReason dev_reason = NM_DEVICE_STATE_REASON_NONE;
-
-                               /* Since the config we're setting is also the device's current
-                                * config, have to ref the config to ensure it doesn't get
-                                * destroyed when the device unrefs it in nm_device_set_ip4_config().
-                                */
-                               nm_device_set_ip4_config (priv->parent_dev,
-                                                         g_object_ref (dev_ip4_config),
-                                                         &dev_reason);
-                       }
-               }
-
-               if (priv->banner) {
-                       g_free (priv->banner);
-                       priv->banner = NULL;
-               }
+               vpn_cleanup (connection);
                break;
        default:
                break;
@@ -987,7 +1034,13 @@ dispose (GObject *object)
 
        cleanup_secrets_dbus_call (NM_VPN_CONNECTION (object));
 
+       if (priv->gw_route)
+               rtnl_route_put (priv->gw_route);
+
        if (priv->parent_dev) {
+               if (priv->device_ip4)
+                       g_signal_handler_disconnect (priv->parent_dev, priv->device_ip4);
+
                if (priv->device_monitor)
                        g_signal_handler_disconnect (priv->parent_dev, priv->device_monitor);