Package: openvpn
Version: 2.2.1-8
Severity: normal
Tags: patch

Hi,

When using tun-ipv6 and pulling IPv6 routes from the server and a restart
(e.g. ping-restart timeout or SIGUSR1) happens, then OpenVPN simply appends the
pulled IPv6 routes to the list of routes present before the restart. IOW, for
every restart (and assuming the configuration hasn't changed on either end),
the same IPv6 routes are appended to the route list again and again. This has
two side-effects:

a) OpenVPN makes an attempt to insert duplicate routes in the system which
   fails with an error message.
b) As the maximum number of routes is (by default) 100, after a number of soft
   restarts OpenVPN exits stating that it cannot handle more than max-routes.

To illustrate a) with an example:

Server configuration:
tun-ipv6
push route-ipv6 2001:db8:42::/48
...

Client configuration:
tun-ipv6
pull
...

Client output:
Tue Nov  6 23:36:33 2012 add_route_ipv6(2001:db8:42::/48 -> 2001:db8:42:100::1 
metric 0) dev tun0
...
Tue Nov  6 23:39:17 2012 SIGUSR1[hard,] received, process restarting
...
Tue Nov  6 23:39:26 2012 add_route_ipv6(2001:db8:42::/48 -> 2001:db8:42:100::1 
metric 0) dev tun0
Tue Nov  6 23:39:26 2012 add_route_ipv6(2001:db8:42::/48 -> 2001:db8:42:100::1 
metric 0) dev tun0
SIOCADDRT: File exists
Tue Nov  6 23:39:26 2012 ERROR: Linux route -6/-A inet6 add command failed: 
external program exited with error status: 7
...
Tue Nov  6 23:40:09 2012 SIGUSR1[hard,] received, process restarting
...
Tue Nov  6 23:40:18 2012 add_route_ipv6(2001:db8:42::/48 -> 2001:db8:42:100::1 
metric 0) dev tun0
Tue Nov  6 23:40:18 2012 add_route_ipv6(2001:db8:42::/48 -> 2001:db8:42:100::1 
metric 0) dev tun0
SIOCADDRT: File exists
Tue Nov  6 23:40:18 2012 ERROR: Linux route -6/-A inet6 add command failed: 
external program exited with error status: 7
Tue Nov  6 23:40:18 2012 add_route_ipv6(2001:db8:42::/48 -> 2001:db8:42:100::1 
metric 0) dev tun0
SIOCADDRT: File exists
Tue Nov  6 23:40:18 2012 ERROR: Linux route -6/-A inet6 add command failed: 
external program exited with error status: 7


For IPv4 routes, OpenVPN uses pre_pull_save() (see options.c) to save the
routes before augmenting them with the pulled ones, and then uses
pre_pull_restore() after every restart to restore the route list in a pristine
state containing only the routes defined in the local configuration. It seems
however that the IPv6 payload patch included in Debian's version does not
provide the same functionality for IPv6 routes. The attached patch fixes this.

Regards,
Apollon

-- System Information:
Debian Release: wheezy/sid
  APT prefers testing
  APT policy: (500, 'testing'), (99, 'unstable'), (1, 'experimental')
Architecture: i386 (i686)

Kernel: Linux 3.2.0-3-686-pae (SMP w/2 CPU cores)
Locale: LANG=el_GR.UTF-8, LC_CTYPE=el_GR.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages openvpn depends on:
ii  debconf [debconf-2.0]  1.5.46
ii  initscripts            2.88dsf-32
ii  libc6                  2.13-35
ii  liblzo2-2              2.06-1
ii  libpam0g               1.1.3-7.1
ii  libpkcs11-helper1      1.09-1
ii  libssl1.0.0            1.0.1c-4
ii  net-tools              1.60-24.1

openvpn recommends no packages.

Versions of packages openvpn suggests:
ii  openssl     1.0.1c-4
ii  resolvconf  1.67

-- Configuration Files:
/etc/default/openvpn changed [not included]

-- debconf information excluded
--- a/route.c
+++ b/route.c
@@ -88,6 +88,15 @@
   return ret;
 }
 
+struct route_ipv6_option_list *
+clone_route_ipv6_option_list (const struct route_ipv6_option_list *src, struct gc_arena *a)
+{
+  const size_t rl6_size = array_mult_safe (sizeof(struct route_ipv6_option), src->capacity, sizeof(struct route_ipv6_option_list));
+  struct route_ipv6_option_list *ret = gc_malloc (rl6_size, false, a);
+  memcpy (ret, src, rl6_size);
+  return ret;
+}
+
 void
 copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src)
 {
@@ -97,6 +106,15 @@
   memcpy (dest, src, src_size);
 }
 
+void
+copy_route_ipv6_option_list (struct route_ipv6_option_list *dest, const struct route_ipv6_option_list *src)
+{
+  const size_t src_size = array_mult_safe (sizeof(struct route_ipv6_option), src->capacity, sizeof(struct route_ipv6_option_list));
+  if (src->n > dest->capacity)
+    msg (M_FATAL, PACKAGE_NAME " ROUTE-IPv6: (copy) number of route options in src (%d) is greater than route list capacity in dest (%d)", src->n, dest->capacity);
+  memcpy (dest, src, src_size);
+}
+
 struct route_list *
 new_route_list (const int max_routes, struct gc_arena *a)
 {
--- a/options.c
+++ b/options.c
@@ -2548,6 +2548,11 @@
 	  o->pre_pull->routes = clone_route_option_list(o->routes, &o->gc);
 	  o->pre_pull->routes_defined = true;
 	}
+      if (o->routes_ipv6)
+        {
+	  o->pre_pull->routes_ipv6 = clone_route_ipv6_option_list(o->routes_ipv6, &o->gc);
+	  o->pre_pull->routes_ipv6_defined = true;
+	}
     }
 }
 
@@ -2569,6 +2574,14 @@
       else
 	o->routes = NULL;
 
+      if (pp->routes_ipv6_defined)
+        {
+	  rol6_check_alloc (o);
+	  copy_route_ipv6_option_list (o->routes_ipv6, pp->routes_ipv6);
+	}
+      else
+        o->routes_ipv6 = NULL;
+
       o->foreign_option_index = pp->foreign_option_index;
     }
 
--- a/options.h
+++ b/options.h
@@ -67,6 +67,9 @@
   bool routes_defined;
   struct route_option_list *routes;
 
+  bool routes_ipv6_defined;
+  struct route_ipv6_option_list *routes_ipv6;
+
   int foreign_option_index;
 };
 
--- a/route.h
+++ b/route.h
@@ -169,7 +169,9 @@
 struct route_option_list *new_route_option_list (const int max_routes, struct gc_arena *a);
 struct route_ipv6_option_list *new_route_ipv6_option_list (const int max_routes, struct gc_arena *a);
 struct route_option_list *clone_route_option_list (const struct route_option_list *src, struct gc_arena *a);
+struct route_ipv6_option_list *clone_route_ipv6_option_list (const struct route_ipv6_option_list *src, struct gc_arena *a);
 void copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src);
+void copy_route_ipv6_option_list (struct route_ipv6_option_list *dest, const struct route_ipv6_option_list *src);
 
 struct route_list *new_route_list (const int max_routes, struct gc_arena *a);
 struct route_ipv6_list *new_route_ipv6_list (const int max_routes, struct gc_arena *a);

Reply via email to