2008-06-06 Dan Williams <dcbw@redhat.com>

Patch from Tambet Ingo  <tambet@gmail.com>

	* src/NetworkManagerSystem.c
	  src/NetworkManagerSystem.h
		- (nm_system_device_add_ip4_route_via_device_with_iface): remove
		- (nm_system_device_set_from_ip4_config): remove unused route_to_iface
		- (nm_system_device_set_ip4_route): clean up
		- (nm_system_vpn_device_set_from_ip4_config): clean up, add VPN routes

	* src/nm-device.c
		- (nm_device_set_ip4_config): remove unused route_to_iface bits

	* src/vpn-manager/nm-vpn-connection.c
		- (ip_address_to_string): new function
		- (print_vpn_config): use ip_address_to_string
		- (merge_vpn_routes): add user-defined routes to the ip4 config
		- (nm_vpn_connection_ip4_config_get): add routes the VPN server sent

	* include/NetworkManagerVPN.h
		- Add 'routes' key



git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@3728 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
This commit is contained in:
Dan Williams 2008-06-06 15:35:45 +00:00
parent 28f5bbeddf
commit 7b1882b583
6 changed files with 262 additions and 232 deletions

View file

@ -1,3 +1,26 @@
2008-06-06 Dan Williams <dcbw@redhat.com>
Patch from Tambet Ingo <tambet@gmail.com>
* src/NetworkManagerSystem.c
src/NetworkManagerSystem.h
- (nm_system_device_add_ip4_route_via_device_with_iface): remove
- (nm_system_device_set_from_ip4_config): remove unused route_to_iface
- (nm_system_device_set_ip4_route): clean up
- (nm_system_vpn_device_set_from_ip4_config): clean up, add VPN routes
* src/nm-device.c
- (nm_device_set_ip4_config): remove unused route_to_iface bits
* src/vpn-manager/nm-vpn-connection.c
- (ip_address_to_string): new function
- (print_vpn_config): use ip_address_to_string
- (merge_vpn_routes): add user-defined routes to the ip4 config
- (nm_vpn_connection_ip4_config_get): add routes the VPN server sent
* include/NetworkManagerVPN.h
- Add 'routes' key
2008-06-05 Dan Williams <dcbw@redhat.com>
Patch from Markus Becker <mab@comnets.uni-bremen.de>

View file

@ -116,5 +116,6 @@ typedef enum NMVPNConnectionStateReason
#define NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV "tundev"
#define NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN "domain"
#define NM_VPN_PLUGIN_IP4_CONFIG_BANNER "banner"
#define NM_VPN_PLUGIN_IP4_CONFIG_ROUTES "routes"
#endif /* NETWORK_MANAGER_VPN_H */

View file

@ -61,134 +61,145 @@
#include <netlink/utils.h>
#include <netlink/route/link.h>
/*
* nm_system_device_set_ip4_route
*
*/
static gboolean
nm_system_device_set_ip4_route (const char *iface,
NMIP4Config *iface_config,
guint32 ip4_gateway,
guint32 ip4_dest,
int prefix,
int mss)
route_in_same_subnet (NMIP4Config *config, guint32 dest, guint32 netmask)
{
gboolean success = FALSE;
struct rtnl_route *route = NULL;
struct rtnl_route *route2 = NULL;
struct nl_handle *nlh = NULL;
struct nl_addr *gw_addr = NULL;
struct nl_addr *dest_addr = NULL;
int err, iface_idx, i;
int num;
int i;
/*
* Zero is not a legal gateway and the ioctl will fail. But zero is a
* way of saying "no route" so we just return here. Hopefully the
* caller flushed the routes, first.
*/
if (ip4_gateway == 0)
return TRUE;
num = nm_ip4_config_get_num_addresses (config);
for (i = 0; i < num; i++) {
const NMSettingIP4Address *cfg_addr;
/*
* Do not add the route if the destination is on the same subnet.
*/
if (iface_config) {
for (i = 0; i < nm_ip4_config_get_num_addresses (iface_config); i++) {
const NMSettingIP4Address *cfg_addr;
cfg_addr = nm_ip4_config_get_address (config, i);
if ((dest & netmask) == (cfg_addr->address & cfg_addr->netmask))
return TRUE;
}
cfg_addr = nm_ip4_config_get_address (iface_config, i);
if ((ip4_dest & cfg_addr->netmask) == (cfg_addr->address & cfg_addr->netmask))
return TRUE;
return FALSE;
}
static struct rtnl_route *
create_route (int iface_idx, int mss)
{
struct rtnl_route *route;
route = rtnl_route_alloc ();
if (route) {
rtnl_route_set_oif (route, iface_idx);
if (mss && rtnl_route_set_metric (route, RTAX_ADVMSS, mss) < 0)
nm_warning ("Could not set mss");
} else
nm_warning ("Could not allocate route");
return route;
}
static int
netmask_to_prefix (guint32 netmask)
{
guchar *p;
guchar *end;
int prefix = 0;
p = (guchar *) &netmask;
end = p + sizeof (guint32);
while ((*p == 0xFF) && p < end) {
prefix += 8;
p++;
}
if (p < end) {
guchar v = *p;
while (v) {
prefix++;
v <<= 1;
}
}
return prefix;
}
static void
nm_system_device_set_ip4_route (const char *iface,
NMIP4Config *iface_config,
guint32 ip4_dest,
guint32 ip4_netmask,
guint32 ip4_gateway,
int mss)
{
struct nl_handle *nlh;
struct rtnl_route *route;
struct nl_addr *dest_addr;
struct nl_addr *gw_addr = NULL;
int err, iface_idx;
if (iface_config && route_in_same_subnet (iface_config, ip4_dest, ip4_netmask))
return;
nlh = nm_netlink_get_default_handle ();
g_return_val_if_fail (nlh != NULL, FALSE);
g_return_if_fail (nlh != NULL);
iface_idx = nm_netlink_iface_to_index (iface);
g_return_val_if_fail (iface_idx >= 0, FALSE);
g_return_if_fail (iface_idx >= 0);
route = rtnl_route_alloc ();
g_return_val_if_fail (route != NULL, FALSE);
rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE);
rtnl_route_set_oif (route, iface_idx);
gw_addr = nl_addr_build (AF_INET, &ip4_gateway, sizeof (ip4_gateway));
if (gw_addr == NULL)
goto out;
rtnl_route_set_gateway (route, gw_addr);
route = create_route (iface_idx, mss);
g_return_if_fail (route != NULL);
/* Destination */
dest_addr = nl_addr_build (AF_INET, &ip4_dest, sizeof (ip4_dest));
if (dest_addr == NULL)
goto out;
nl_addr_set_prefixlen (dest_addr, prefix);
g_return_if_fail (dest_addr != NULL);
nl_addr_set_prefixlen (dest_addr, netmask_to_prefix (ip4_netmask));
rtnl_route_set_dst (route, dest_addr);
nl_addr_put (dest_addr);
if (mss) {
if (rtnl_route_set_metric (route, RTAX_ADVMSS, mss) < 0)
goto out;
/* Gateway */
if (ip4_gateway) {
gw_addr = nl_addr_build (AF_INET, &ip4_gateway, sizeof (ip4_gateway));
if (gw_addr) {
rtnl_route_set_gateway (route, gw_addr);
rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE);
} else {
nm_warning ("Invalid gateway");
rtnl_route_put (route);
return;
}
}
/* Add the route */
err = rtnl_route_add (nlh, route, 0);
if (err == 0) {
/* Everything good */
success = TRUE;
goto out;
if (err == ESRCH && ip4_gateway) {
/* Gateway might be over a bridge; try adding a route to gateway first */
struct rtnl_route *route2;
route2 = create_route (iface_idx, mss);
if (route2) {
/* Add route to gateway over bridge */
rtnl_route_set_dst (route2, gw_addr);
err = rtnl_route_add (nlh, route2, 0);
if (!err) {
/* Try adding the route again */
err = rtnl_route_add (nlh, route, 0);
if (err)
rtnl_route_del (nlh, route2, 0);
}
rtnl_route_put (route2);
}
}
if (err != ESRCH) {
nm_warning ("Failed to set IPv4 default route on '%s': %s",
iface,
nl_geterror ());
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 (err)
nm_warning ("Failed to set IPv4 route on '%s': %s", iface, nl_geterror ());
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 ());
goto out;
}
/* Try adding the route again */
err = rtnl_route_add (nlh, route, 0);
if (!err) {
success = TRUE;
} else {
rtnl_route_del (nlh, route2, 0);
nm_warning ("Failed to set IPv4 default route on '%s': %s",
iface,
nl_geterror ());
}
out:
rtnl_route_put (route);
if (gw_addr)
nl_addr_put (gw_addr);
if (route2)
rtnl_route_put (route2);
if (route)
rtnl_route_put (route);
return success;
}
typedef struct {
const char *iface;
int ifindex;
@ -275,33 +286,6 @@ add_ip4_addresses (NMIP4Config *config, const char *iface)
return TRUE;
}
static int
netmask_to_prefix (guint32 netmask)
{
guchar *p;
guchar *end;
int prefix = 0;
p = (guchar *) &netmask;
end = p + sizeof (guint32);
while ((*p == 0xFF) && p < end) {
prefix += 8;
p++;
}
if (p < end) {
guchar v = *p;
while (v) {
prefix++;
v <<= 1;
}
}
return prefix;
}
/*
* nm_system_device_set_from_ip4_config
*
@ -310,8 +294,7 @@ netmask_to_prefix (guint32 netmask)
*/
gboolean
nm_system_device_set_from_ip4_config (const char *iface,
NMIP4Config *config,
gboolean route_to_iface)
NMIP4Config *config)
{
int len, i;
@ -328,9 +311,9 @@ nm_system_device_set_from_ip4_config (const char *iface,
const NMSettingIP4Address *route = nm_ip4_config_get_static_route (config, i);
nm_system_device_set_ip4_route (iface, config,
route->gateway,
route->address,
netmask_to_prefix (route->netmask),
route->netmask,
route->gateway,
nm_ip4_config_get_mss (config));
}
@ -349,11 +332,11 @@ nm_system_device_set_from_ip4_config (const char *iface,
gboolean
nm_system_vpn_device_set_from_ip4_config (NMDevice *active_device,
const char *iface,
NMIP4Config *config,
GSList *routes)
NMIP4Config *config)
{
NMIP4Config *ad_config = NULL;
NMNamedManager *named_mgr;
int num;
int i;
g_return_val_if_fail (config != NULL, FALSE);
@ -363,7 +346,8 @@ nm_system_vpn_device_set_from_ip4_config (NMDevice *active_device,
guint32 ad_gw = 0, vpn_gw = 0;
const NMSettingIP4Address *tmp;
for (i = 0; i < nm_ip4_config_get_num_addresses (ad_config); i++) {
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;
@ -371,17 +355,20 @@ nm_system_vpn_device_set_from_ip4_config (NMDevice *active_device,
}
}
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;
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, ad_gw, vpn_gw, 32,
nm_ip4_config_get_mss (config));
nm_system_device_set_ip4_route (nm_device_get_ip_iface (active_device),
ad_config, vpn_gw, 0xFFFFFFFF, ad_gw,
nm_ip4_config_get_mss (config));
}
}
if (!iface || !strlen (iface))
@ -396,15 +383,21 @@ nm_system_vpn_device_set_from_ip4_config (NMDevice *active_device,
if (nm_ip4_config_get_mtu (config))
nm_system_device_set_mtu (iface, nm_ip4_config_get_mtu (config));
if (g_slist_length (routes) == 0) {
nm_system_device_replace_default_ip4_route (iface, 0, 0);
} else {
GSList *iter;
/* Set routes */
num = nm_ip4_config_get_num_static_routes (config);
for (i = 0; i < num; i++) {
const NMSettingIP4Address *route = nm_ip4_config_get_static_route (config, i);
for (iter = routes; iter; iter = iter->next)
nm_system_device_add_ip4_route_via_device_with_iface (iface, (char *) iter->data);
nm_system_device_set_ip4_route (iface, config,
route->address,
route->netmask,
route->gateway,
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, config, NM_NAMED_IP_CONFIG_TYPE_VPN);
@ -551,45 +544,6 @@ nm_system_device_set_mtu (const char *iface, guint32 mtu)
return success;
}
/*
* nm_system_device_add_ip4_route_via_device_with_iface
*
* Add route to the given device
*
*/
void nm_system_device_add_ip4_route_via_device_with_iface (const char *iface, const char *addr)
{
struct rtnl_route *route;
struct nl_handle *nlh;
struct nl_addr *dst;
int iface_idx, err;
nlh = nm_netlink_get_default_handle ();
g_return_if_fail (nlh != NULL);
route = rtnl_route_alloc ();
g_return_if_fail (route != NULL);
iface_idx = nm_netlink_iface_to_index (iface);
if (iface_idx < 0)
goto out;
rtnl_route_set_oif (route, iface_idx);
if (!(dst = nl_addr_parse (addr, AF_INET)))
goto out;
rtnl_route_set_dst (route, dst);
nl_addr_put (dst);
err = rtnl_route_add (nlh, route, 0);
if (err) {
nm_warning ("rtnl_route_add() returned error %s (%d)\n%s",
strerror (err), err, nl_geterror());
}
out:
rtnl_route_put (route);
}
/*
* nm_system_replace_default_ip4_route
*
@ -803,4 +757,3 @@ void nm_system_device_flush_ip4_routes_with_iface (const char *iface)
nl_cache_free (route_cache);
}

View file

@ -41,8 +41,6 @@ void nm_system_device_replace_default_ip4_route (const char *iface,
guint32 gw,
guint32 mss);
void nm_system_device_add_ip4_route_via_device_with_iface (const char *iface, const char *route);
void nm_system_device_flush_ip4_addresses (NMDevice *dev);
void nm_system_device_flush_ip4_addresses_with_iface (const char *iface);
@ -51,13 +49,11 @@ void nm_system_kill_all_dhcp_daemons (void);
void nm_system_update_dns (void);
gboolean nm_system_device_set_from_ip4_config (const char *iface,
NMIP4Config *config,
gboolean route_to_iface);
NMIP4Config *config);
gboolean nm_system_vpn_device_set_from_ip4_config (NMDevice *active_device,
const char *iface,
NMIP4Config *config,
GSList *routes);
NMIP4Config *config);
gboolean nm_system_vpn_device_unset_from_ip4_config (NMDevice *active_device,
const char *iface,

View file

@ -1355,7 +1355,6 @@ nm_device_set_ip4_config (NMDevice *self, NMIP4Config *config)
{
NMDevicePrivate *priv;
const char *ip_iface;
gboolean route_to_iface;
gboolean success;
g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
@ -1381,16 +1380,7 @@ nm_device_set_ip4_config (NMDevice *self, NMIP4Config *config)
ip_iface = nm_device_get_ip_iface (self);
/* 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. */
if (strcmp (ip_iface, nm_device_get_iface (self)))
route_to_iface = TRUE;
else
route_to_iface = FALSE;
success = nm_system_device_set_from_ip4_config (ip_iface, config, route_to_iface);
success = nm_system_device_set_from_ip4_config (ip_iface, config);
if (success) {
nm_device_update_ip4_address (self);
nm_system_set_hostname (config);

View file

@ -27,6 +27,8 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include "NetworkManager.h"
#include "NetworkManagerVPN.h"
@ -228,17 +230,6 @@ nm_vpn_connection_get_service (NMVPNConnection *connection)
return setting->service_type;
}
static GSList *
nm_vpn_connection_get_routes (NMVPNConnection *connection)
{
NMSettingVPN *setting;
setting = (NMSettingVPN *) nm_connection_get_setting (NM_VPN_CONNECTION_GET_PRIVATE (connection)->connection,
NM_TYPE_SETTING_VPN);
return setting->routes;
}
static void
plugin_state_changed (DBusGProxy *proxy,
NMVPNServiceState state,
@ -266,13 +257,21 @@ plugin_state_changed (DBusGProxy *proxy,
}
}
static const char *
ip_address_to_string (guint32 numeric)
{
struct in_addr temp_addr;
temp_addr.s_addr = numeric;
return inet_ntoa (temp_addr);
}
static void
print_vpn_config (NMIP4Config *config,
const char *tundev,
const char *banner)
{
const NMSettingIP4Address *addr;
struct in_addr temp_addr;
char * dns_domain = NULL;
guint32 num;
guint32 i;
@ -280,23 +279,28 @@ print_vpn_config (NMIP4Config *config,
g_return_if_fail (config != NULL);
addr = nm_ip4_config_get_address (config, 0);
temp_addr.s_addr = addr->gateway;
nm_info ("VPN Gateway: %s", inet_ntoa (temp_addr));
nm_info ("VPN Gateway: %s", ip_address_to_string (addr->gateway));
nm_info ("Tunnel Device: %s", tundev);
temp_addr.s_addr = addr->address;
nm_info ("Internal IP4 Address: %s", inet_ntoa (temp_addr));
temp_addr.s_addr = addr->netmask;
nm_info ("Internal IP4 Netmask: %s", inet_ntoa (temp_addr));
temp_addr.s_addr = nm_ip4_config_get_ptp_address (config);
nm_info ("Internal IP4 Point-to-Point Address: %s", inet_ntoa (temp_addr));
nm_info ("Internal IP4 Address: %s", ip_address_to_string (addr->address));
nm_info ("Internal IP4 Netmask: %s", ip_address_to_string (addr->netmask));
nm_info ("Internal IP4 Point-to-Point Address: %s",
ip_address_to_string (nm_ip4_config_get_ptp_address (config)));
nm_info ("Maximum Segment Size (MSS): %d", nm_ip4_config_get_mss (config));
num = nm_ip4_config_get_num_nameservers (config);
num = nm_ip4_config_get_num_static_routes (config);
for (i = 0; i < num; i++) {
temp_addr.s_addr = nm_ip4_config_get_nameserver (config, i);
nm_info ("Internal IP4 DNS: %s", inet_ntoa (temp_addr));
addr = nm_ip4_config_get_static_route (config, i);
nm_info ("Static Route: %s/%s Gateway: %s",
ip_address_to_string (addr->address),
ip_address_to_string (addr->netmask),
ip_address_to_string (addr->gateway));
}
num = nm_ip4_config_get_num_nameservers (config);
for (i = 0; i < num; i++)
nm_info ("Internal IP4 DNS: %s", ip_address_to_string (nm_ip4_config_get_nameserver (config, i)));
if (nm_ip4_config_get_num_domains (config) > 0)
dns_domain = (char *) nm_ip4_config_get_domain (config, 0);
nm_info ("DNS Domain: '%s'", dns_domain ? dns_domain : "(none)");
@ -306,6 +310,57 @@ print_vpn_config (NMIP4Config *config,
nm_info ("-----------------------------------------");
}
static void
merge_vpn_routes (NMVPNConnection *connection, NMIP4Config *config)
{
NMSettingVPN *setting;
GSList *iter;
setting = NM_SETTING_VPN (nm_connection_get_setting (NM_VPN_CONNECTION_GET_PRIVATE (connection)->connection,
NM_TYPE_SETTING_VPN));
/* FIXME: Shouldn't the routes from user (NMSettingVPN) be inserted in the beginning
instead of appending to the end?
*/
for (iter = setting->routes; iter; iter = iter->next) {
struct in_addr tmp;
char *p, *route;
long int prefix = 32;
route = g_strdup ((char *) iter->data);
p = strchr (route, '/');
if (!p || !(*(p + 1))) {
nm_warning ("Ignoring invalid route '%s'", route);
goto next;
}
errno = 0;
prefix = strtol (p + 1, NULL, 10);
if (errno || prefix < 0 || prefix > 32) {
nm_warning ("Ignoring invalid route '%s'", route);
goto next;
}
/* don't pass the prefix to inet_pton() */
*p = '\0';
if (inet_pton (AF_INET, route, &tmp) > 0) {
NMSettingIP4Address *addr;
addr = g_new0 (NMSettingIP4Address, 1);
addr->address = tmp.s_addr;
addr->netmask = ntohl (0xFFFFFFFF << (32 - prefix));
addr->gateway = 0;
nm_ip4_config_take_static_route (config, addr);
} else
nm_warning ("Ignoring invalid route '%s'", route);
next:
g_free (route);
}
}
static void
nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
GHashTable *config_hash,
@ -395,6 +450,18 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
priv->banner = g_strdup (g_value_get_string (val));
}
val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES);
if (val) {
GSList *routes;
GSList *iter;
routes = nm_utils_ip4_addresses_from_gvalue (val);
for (iter = routes; iter; iter = iter->next)
nm_ip4_config_take_static_route (config, (NMSettingIP4Address *) iter->data);
g_slist_free (routes);
}
print_vpn_config (config, priv->tundev, priv->banner);
priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
@ -403,9 +470,9 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
/* 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);
merge_vpn_routes (connection, config);
if (nm_system_vpn_device_set_from_ip4_config (priv->parent_dev, priv->tundev, priv->ip4_config,
nm_vpn_connection_get_routes (connection))) {
if (nm_system_vpn_device_set_from_ip4_config (priv->parent_dev, priv->tundev, priv->ip4_config)) {
nm_info ("VPN connection '%s' (IP Config Get) complete.",
nm_vpn_connection_get_name (connection));
nm_vpn_connection_set_vpn_state (connection,