Re: NetworkManager VPN and routing in bridged mode



Hello,

The attached file is the patch against NETWORKMANAGER_0_6_5_RELEASE (rev
2558) which implements the logic for tap-based VPN networks. The code is
in "works for me" state. I understand that NM 0.6.5 isn't best codebase
but it's the version I use (which helps to test the patch) and given its
relatively small size it should be easy to port it to recently tagged 0_6_6.

Any feedback will be greatly appreciated.

Regards,
Valentine Sinitsyn

Index: src/nm-ip4-config.c
===================================================================
--- src/nm-ip4-config.c	(revision 3427)
+++ src/nm-ip4-config.c	(working copy)
@@ -16,6 +16,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
+ * (C) Copyright 2008 Valentine Sinitsyn <e_val inbox ru>   
  * (C) Copyright 2005 Red Hat, Inc.
  */
 
@@ -43,6 +44,8 @@
 
 	guint32	mtu;	/* Maximum Transmission Unit of the interface */
 	guint32	mss;	/* Maximum Segment Size of the route */
+	
+	guint32 remote_gateway; /* Used for VPN networks with tap-style virtual device */
 
 	GSList *	nameservers;
 	GSList *	domains;
@@ -56,6 +59,7 @@
 	 * an IP4Config before it can be used.
 	 */
 	gboolean	secondary;
+
 };
 
 
@@ -357,6 +361,20 @@
 	config->mss = mss;
 }
 
+guint32	nm_ip4_config_get_remote_gateway (NMIP4Config *config)
+{
+	g_return_val_if_fail (config != NULL, 0);
+
+	return config->remote_gateway;
+}
+
+void nm_ip4_config_set_remote_gateway (NMIP4Config *config, guint32 remote_gateway)
+{
+	g_return_if_fail (config != NULL);
+
+	config->remote_gateway = remote_gateway;
+}
+
 /* libnl convenience/conversion functions */
 
 static int ip4_addr_to_rtnl_local (guint32 ip4_address, struct rtnl_addr *addr)
Index: src/nm-ip4-config.h
===================================================================
--- src/nm-ip4-config.h	(revision 3427)
+++ src/nm-ip4-config.h	(working copy)
@@ -16,6 +16,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
+ * (C) Copyright 2008 Valentine Sinitsyn <e_val inbox ru>   
  * (C) Copyright 2004 Red Hat, Inc.
  */
 
@@ -74,6 +75,9 @@
 guint32		nm_ip4_config_get_mss			(NMIP4Config *config);
 void			nm_ip4_config_set_mss			(NMIP4Config *config, guint32 mss);
 
+guint32		nm_ip4_config_get_remote_gateway	(NMIP4Config *config);
+void			nm_ip4_config_set_remote_gateway	(NMIP4Config *config, guint32 remote_gateway);
+
 /* Flags for nm_ip4_config_to_rtnl_addr() */
 #define NM_RTNL_ADDR_NONE		0x0000
 #define NM_RTNL_ADDR_ADDR		0x0001
Index: src/vpn-manager/nm-vpn-service.c
===================================================================
--- src/vpn-manager/nm-vpn-service.c	(revision 3427)
+++ src/vpn-manager/nm-vpn-service.c	(working copy)
@@ -16,6 +16,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
+ * (C) Copyright 2008 Valentine Sinitsyn <e_val inbox ru>  
  * (C) Copyright 2005 Red Hat, Inc.
  */
 
@@ -841,6 +842,17 @@
 	if (!get_dbus_string_helper (&iter, &login_banner, "Login Banner"))
 		goto out;
 
+	/* Eleventh arg: Remote VPN Gateway (UINT32) 
+	 * For now, only OpenVPN service knows about it so we won't complain
+	 * if it is absent from the message.
+	 */
+	if (dbus_message_iter_next (&iter)
+	    && (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_UINT32))
+	{    
+		dbus_message_iter_get_basic (&iter, &num);
+		nm_ip4_config_set_remote_gateway (config, num);
+	}	
+
 #ifdef NM_DEBUG_VPN_CONFIG
 	print_vpn_config (ip4_vpn_gateway,
 	                  tundev,
Index: src/NetworkManagerSystem.c
===================================================================
--- src/NetworkManagerSystem.c	(revision 3427)
+++ src/NetworkManagerSystem.c	(working copy)
@@ -1,6 +1,7 @@
 /* NetworkManager -- Network link manager
  *
  * Dan Williams <dcbw redhat com>
+
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -16,6 +17,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
+ * Copyright (C) 2008 Valentine Sinitsyn <e_val inbox ru> 
  * Copyright (C) 2004 Red Hat, Inc.
  * Copyright (C) 1996 - 1997 Yoichi Hariguchi <yoichi fore com>
  * Copyright (C) January, 1998 Sergei Viznyuk <sv phystech com>
@@ -51,7 +53,123 @@
 #include <netlink/utils.h>
 #include <netlink/route/link.h>
 
+/*
+ * nm_system_device_set_ip4_route_with_iface
+ *
+ */
+static gboolean nm_system_device_set_ip4_route_with_iface (const char *iface, int ip4_gateway, int ip4_dest, int ip4_netmask, int mss)
+{
+	NMSock *			sk;
+	gboolean			success = FALSE;
+	struct rtentry		rtent;
+	struct sockaddr_in *p;
+	int				err;
 
+	/*
+	 * 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;
+
+	if ((sk = nm_dev_sock_open (NULL, NETWORK_CONTROL, __FUNCTION__, NULL)) == NULL)
+		return FALSE;
+
+	memset (&rtent, 0, sizeof (struct rtentry));
+	p				= (struct sockaddr_in *) &rtent.rt_dst;
+	p->sin_family		= AF_INET;
+	p->sin_addr.s_addr	= ip4_dest;
+	p				= (struct sockaddr_in *) &rtent.rt_gateway;
+	p->sin_family		= AF_INET;
+	p->sin_addr.s_addr	= ip4_gateway;
+	p				= (struct sockaddr_in *) &rtent.rt_genmask;
+	p->sin_family		= AF_INET;
+	p->sin_addr.s_addr	= ip4_netmask;
+	rtent.rt_dev		= (char *)iface;
+	rtent.rt_metric	= 1;
+	rtent.rt_window	= 0;
+	rtent.rt_flags		= RTF_UP | RTF_GATEWAY | (rtent.rt_window ? RTF_WINDOW : 0);
+
+	if (mss)
+	{
+		rtent.rt_flags |= RTF_MTU;
+		rtent.rt_mtu = mss;
+	}
+
+#ifdef IOCTL_DEBUG
+	nm_info ("%s: About to CADDRT\n", nm_device_get_iface (dev));
+#endif
+	err = ioctl (nm_dev_sock_get_fd (sk), SIOCADDRT, &rtent);
+#ifdef IOCTL_DEBUG
+	nm_info ("%s: About to CADDRT\n", nm_device_get_iface (dev));
+#endif
+
+	if (err == -1)
+	{
+		if (errno == ENETUNREACH)	/* possibly gateway is over the bridge */
+		{						/* try adding a route to gateway first */
+			struct rtentry	rtent2;
+			
+			memset (&rtent2, 0, sizeof(struct rtentry));
+			p				= (struct sockaddr_in *)&rtent2.rt_dst;
+			p->sin_family		= AF_INET;
+			p				= (struct sockaddr_in *)&rtent2.rt_gateway;
+			p->sin_family		= AF_INET;
+			p->sin_addr.s_addr	= ip4_gateway;
+			p				= (struct sockaddr_in *)&rtent2.rt_genmask;
+			p->sin_family		= AF_INET;
+			p->sin_addr.s_addr	= 0xffffffff;
+			rtent2.rt_dev		= (char *)iface;
+			rtent2.rt_metric	= 0;
+			rtent2.rt_flags	= RTF_UP | RTF_HOST;
+
+			if (mss)
+			{
+				rtent2.rt_flags |= RTF_MTU;
+				rtent2.rt_mtu = mss;
+			}
+
+#ifdef IOCTL_DEBUG
+			nm_info ("%s: About to CADDRT (2)\n", nm_device_get_iface (dev));
+#endif
+			err = ioctl (nm_dev_sock_get_fd (sk), SIOCADDRT, &rtent2);
+#ifdef IOCTL_DEBUG
+			nm_info ("%s: About to CADDRT (2)\n", nm_device_get_iface (dev));
+#endif
+
+			if (!err)
+			{
+#ifdef IOCTL_DEBUG
+				nm_info ("%s: About to CADDRT (3)\n", nm_device_get_iface (dev));
+#endif
+				err = ioctl (nm_dev_sock_get_fd (sk), SIOCADDRT, &rtent);
+#ifdef IOCTL_DEBUG
+				nm_info ("%s: About to CADDRT (3)\n", nm_device_get_iface (dev));
+#endif
+
+				if (!err)
+					success = TRUE;
+				else
+					nm_warning ("Failed to set IPv4 default route on '%s': %s", iface, strerror (errno));
+			}
+		}
+		else
+			nm_warning ("Failed to set IPv4 default route on '%s': %s", iface, strerror (errno));
+	}
+	else
+		success = TRUE;
+
+	nm_dev_sock_close (sk);
+	return success;
+}
+
+static gboolean nm_system_device_set_ip4_route (NMDevice *dev, int ip4_gateway, int ip4_dest, int ip4_netmask, int mss)
+{
+	return nm_system_device_set_ip4_route_with_iface (nm_device_get_iface (dev), ip4_gateway, ip4_dest, ip4_netmask, mss);
+}
+
+#if 0
 /*
  * nm_system_device_set_ip4_route
  *
@@ -165,8 +283,8 @@
 	nm_dev_sock_close (sk);
 	return success;
 }
+#endif
 
-
 static struct nl_cache * get_link_cache (struct nl_handle *nlh)
 {
 	static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
@@ -420,56 +538,81 @@
 	struct nl_handle *	nlh = NULL;
 	struct rtnl_addr *	addr = NULL;
 	struct rtnl_link *	request = NULL;
+	guint32			remote_gateway = 0;
+	guint32			remote_network = 0;
 
 	g_return_val_if_fail (config != NULL, FALSE);
+	
+	remote_gateway = nm_ip4_config_get_remote_gateway (config);
+	if (remote_gateway)
+	    remote_network = (nm_ip4_config_get_address (config) & nm_ip4_config_get_netmask (config));	
 
 	/* 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)))
 		nm_system_device_set_ip4_route (active_device, nm_ip4_config_get_gateway (ad_config), nm_ip4_config_get_gateway (config), 0xFFFFFFFF, nm_ip4_config_get_mss (config));
 
+
 	if (iface != NULL && strlen (iface))
 	{
 		nm_system_device_set_up_down_with_iface (iface, TRUE);
 
-		nlh = new_nl_handle ();
-
-		if ((addr = nm_ip4_config_to_rtnl_addr (config, NM_RTNL_ADDR_PTP_DEFAULT)))
+		if (!remote_gateway)
 		{
-			int err = 0;
-			iface_to_rtnl_index (iface, nlh, addr);
-			if ((err = rtnl_addr_add (nlh, addr, 0)) < 0)
-				nm_warning ("nm_system_device_set_from_ip4_config(): error %d returned from rtnl_addr_add():\n%s", err, nl_geterror());
-			rtnl_addr_put (addr);
-		}
-		else
-			nm_warning ("nm_system_vpn_device_set_from_ip4_config(): couldn't create rtnl address!\n");
+		    nlh = new_nl_handle ();
 
-		/* Set the MTU */
-		if ((request = rtnl_link_alloc ()))
-		{
-			struct rtnl_link * old;
+		    if ((addr = nm_ip4_config_to_rtnl_addr (config, NM_RTNL_ADDR_PTP_DEFAULT)))
+		    {
+			    int err = 0;
+			    iface_to_rtnl_index (iface, nlh, addr);
+			    if ((err = rtnl_addr_add (nlh, addr, 0)) < 0)
+				    nm_warning ("nm_system_device_set_from_ip4_config(): error %d returned from rtnl_addr_add():\n%s", err, nl_geterror());
+			    rtnl_addr_put (addr);
+		    }
+		    else
+		    {
+			    nm_warning ("nm_system_vpn_device_set_from_ip4_config(): couldn't create rtnl address!\n");
+		    }	
 
-			old = iface_to_rtnl_link (iface, nlh);
-			rtnl_link_set_mtu (request, 1412);
-			rtnl_link_change (nlh, old, request, 0);
 
-			rtnl_link_put (old);
-			rtnl_link_put (request);
-		}
+		    /* Set the MTU */
+		    if ((request = rtnl_link_alloc ()))
+		    {
+			    struct rtnl_link * old;
 
-		nl_close (nlh);
-		nl_handle_destroy (nlh);
+			    old = iface_to_rtnl_link (iface, nlh);
+			    rtnl_link_set_mtu (request, 1412);
+			    rtnl_link_change (nlh, old, request, 0);
 
+			    rtnl_link_put (old);
+			    rtnl_link_put (request);
+		    }
+
+		    nl_close (nlh);
+		    nl_handle_destroy (nlh);
+		}    		
+
 		sleep (1);
 
 		nm_system_device_flush_routes_with_iface (iface);
 		if (num_routes <= 0)
 		{
-			nm_system_delete_default_route ();
-			nm_system_device_add_default_route_via_device_with_iface (iface);
+			nm_system_delete_default_route ();		
+			if (!remote_gateway)
+			{
+			    nm_system_device_add_default_route_via_device_with_iface (iface);
+			}
+			else
+			{
+			    /* nm_system_device_set_ip4_route() doesn't accept 0.0.0.0 as gateway,
+			     * so we specify ourselves to make it happy
+			     */
+			    nm_system_device_set_ip4_route_with_iface (iface, nm_ip4_config_get_address (config), remote_network, nm_ip4_config_get_netmask (config), nm_ip4_config_get_mss (config));
+			    nm_system_device_set_ip4_route_with_iface (iface, remote_gateway, 0, 0, nm_ip4_config_get_mss (config));
+			}
 		}
 		else
 		{
+			/* FIXME: This will not work for tap-like devices (see above) */
 			int i;
 			for (i = 0; i < num_routes; i++)
 			{
Index: vpn-daemons/openvpn/src/nm-openvpn-service-openvpn-helper.c
===================================================================
--- vpn-daemons/openvpn/src/nm-openvpn-service-openvpn-helper.c	(revision 3427)
+++ vpn-daemons/openvpn/src/nm-openvpn-service-openvpn-helper.c	(working copy)
@@ -18,6 +18,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
+ * (C) Copyright 2008 Valentine Sinitsyn <e_val inbox ru>   
  * (C) Copyright 2005 Red Hat, Inc.
  * (C) Copyright 2005 Tim Niemueller
  *
@@ -197,6 +198,7 @@
 static gboolean
 send_config_info (DBusConnection *con,
 		  const char *str_vpn_gateway,
+		  const char *str_remote_gateway,
 		  const char *str_tundev,
 		  const char *str_ip4_address,
 		  const char *str_ip4_ptpaddr,
@@ -208,6 +210,7 @@
   DBusMessage *	message;
   struct in_addr	temp_addr;
   guint32		uint_vpn_gateway = 0;
+  guint32               uint_remote_gateway = 0;
   guint32		uint_ip4_address = 0;
   guint32		uint_ip4_ptpaddr = 0;
   guint32		uint_ip4_netmask = 0xFFFFFFFF; /* Default mask of 255.255.255.255 */
@@ -231,6 +234,12 @@
     goto out;
   }
 
+  if (strlen (str_remote_gateway) > 0 && ! ipstr_to_uint32 (str_remote_gateway, &uint_remote_gateway) ) {
+    nm_warning ("nm-openvpn-service-openvpn-helper didn't receive a valid Remote VPN Gateway from openvpn.");
+    send_config_error (con, "Remote VPN Gateway");
+    goto out;
+  }
+
   if (! ipstr_to_uint32 (str_ip4_address, &uint_ip4_address) ) {
     nm_warning ("nm-openvpn-service-openvpn-helper didn't receive a valid Internal IP4 Address from openvpn.");
     send_config_error (con, "IP4 Address");
@@ -257,6 +266,7 @@
 			    DBUS_TYPE_UINT32, &uint_ip4_netmask,
 			    DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &uint_ip4_dns, uint_ip4_dns_len,
 			    DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &uint_ip4_nbns, uint_ip4_nbns_len,
+			    DBUS_TYPE_UINT32, &uint_remote_gateway,
 			    DBUS_TYPE_INVALID);
   if (dbus_connection_send (con, message, NULL))
     success = TRUE;
@@ -304,6 +314,7 @@
   DBusConnection  *con;
   DBusError        error;
   char            *vpn_gateway = NULL;
+  char            *remote_gateway = NULL;
   char            *tundev = NULL;
   char            *ip4_address = NULL;
   char            *ip4_ptp = NULL;
@@ -339,14 +350,15 @@
 
   // print_env();
 
-  vpn_gateway = getenv( "trusted_ip" );
-  tundev      = getenv ("dev");
-  ip4_ptp     = getenv("ifconfig_remote");
-  ip4_address = getenv("ifconfig_local");
-  ip4_netmask = getenv("route_netmask_1");
+  vpn_gateway    = getenv( "trusted_ip" );
+  remote_gateway = getenv( "route_vpn_gateway" );
+  tundev         = getenv ("dev");
+  ip4_ptp        = getenv("ifconfig_remote");
+  ip4_address    = getenv("ifconfig_local");
+  ip4_netmask    = getenv("ifconfig_netmask");
   
-  ip4_dns     = g_ptr_array_new();
-  ip4_nbns    = g_ptr_array_new();
+  ip4_dns        = g_ptr_array_new();
+  ip4_nbns       = g_ptr_array_new();
   
   while (1) {
     sprintf(envname, "foreign_option_%i", i++);
@@ -381,6 +393,7 @@
 	{
 		FILE *file = fopen ("/tmp/vpnstuff", "w");
 		fprintf (file, "VPNGATEWAY: '%s'\n", vpn_gateway);
+		fprintf (file, "REMOTEGATEWAY: '%s'\n", remote_gateway);
 		fprintf (file, "TUNDEV: '%s'\n", tundev);
 		fprintf (file, "IP4_ADDRESS: '%s'\n", ip4_address);
 		fprintf (file, "IP4_NETMASK: '%s'\n", ip4_netmask);
@@ -393,6 +406,17 @@
     send_config_error (con, "VPN Gateway");
     exit (1);
   }
+  if (!remote_gateway) {
+/*    if (ip4_netmask) {
+      nm_warning ("nm-openvpn-service-openvpn-helper received Remote VPN Gateway but no netmask from openvpn.");
+      send_config_error (con, "Remote VPN Gateway");
+      exit (1);
+    } else {
+      remote_gateway = g_strdup("");
+    }
+*/
+    remote_gateway = g_strdup ("");    
+  }
   if (!tundev || !g_utf8_validate (tundev, -1, NULL)) {
     nm_warning ("nm-openvpn-service-openvpn-helper didn't receive a Tunnel Device from openvpn, or the tunnel device was not valid UTF-8.");
     send_config_error (con, "Tunnel Device");
@@ -408,7 +432,7 @@
     ip4_netmask = g_strdup ("");
   }
 
-  if (!send_config_info (con, vpn_gateway, tundev,
+  if (!send_config_info (con, vpn_gateway, remote_gateway, tundev,
 			 ip4_address, ip4_ptp, ip4_netmask,
 			 ip4_dns, ip4_nbns)) {
     exit_code = 1;
Index: vpn-daemons/openvpn/src/nm-openvpn-service.c
===================================================================
--- vpn-daemons/openvpn/src/nm-openvpn-service.c	(revision 3427)
+++ vpn-daemons/openvpn/src/nm-openvpn-service.c	(working copy)
@@ -2,6 +2,7 @@
  *
  * Tim Niemueller <tim niemueller de>
  * Based on work by Dan Williams <dcbw redhat com>
+ * Updated by Valentine Sinitsyn <e_val inbox ru>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -1266,6 +1267,7 @@
   guint32 *		ip4_nbns;
   guint32		ip4_nbns_len;
   guint32		mss;
+  guint32		remote_gateway;
   gboolean		success = FALSE;
   char *                empty = "";
 
@@ -1288,6 +1290,7 @@
 			    DBUS_TYPE_UINT32, &ip4_netmask,
 			    DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &ip4_dns, &ip4_dns_len,
 			    DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &ip4_nbns, &ip4_nbns_len,
+			    DBUS_TYPE_UINT32, &remote_gateway,
 			    DBUS_TYPE_INVALID))
     {
       DBusMessage	*signal;
@@ -1312,6 +1315,7 @@
 				DBUS_TYPE_UINT32, &mss,
 				DBUS_TYPE_STRING, &empty,
 				DBUS_TYPE_STRING, &empty,
+				DBUS_TYPE_UINT32, &remote_gateway,
 				DBUS_TYPE_INVALID);
 
       if (!dbus_connection_send (data->con, signal, NULL))



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]