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()



git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@4277 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
This commit is contained in:
Dan Williams 2008-11-07 13:57:39 +00:00
parent 5fdb74935c
commit 214114fb2d
6 changed files with 231 additions and 121 deletions

View file

@ -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

View 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;
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_ADDRESSES) {
if (!add_ip4_addresses (config, iface))
return FALSE;
sleep (1);
}
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_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 (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);

View 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,

View 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;
if (new_config && old_config)
diff = nm_ip4_config_diff (new_config, old_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 */
named_mgr = nm_named_manager_get ();
nm_named_manager_remove_ip4_config (named_mgr, ip_iface, priv->ip4_config);
g_object_unref (named_mgr);
g_object_unref (priv->ip4_config);
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);

View 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);

View 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);