Hi,

On Sat, Dec 26, 2015 at 11:27:53PM +0100, Arne Schwabe wrote:
> > So, you asked for it, you or Arne get to review this :-) - it's currently
> > missing a man page entry, but the commit message explains how it works -
> > just put
> and a changlog entry :)

Done!

> >   push-remove tun-ipv6
> >   push-remove route-ipv6
> >
> > into your ccd/ file, and all options strings starting with one of these
> > strings are removed.  Caveat: "push-remove route" will remove IPv4 and
> > IPv6 routes, so to remove only IPv4 routes, use
> >
> >   push-remove 'route '
> 
> A sounds like more of a hack that anything.

A hack it is, but one that gives me the maximum flexibility with fairly
little code.  Being able to do

 push-remove 'route-ipv6 2001:'

*and*

 push-remove route

(and the latter only matching "push route" and not "push route-ipv6") is
more complicated...

[..]
> +void
> +push_remove_option (struct options *o, const char *p)
> +{
> +  msg( D_PUSH, "PUSH_REMOVE '%s'", p );
> +
> +  if (o && o->push_list.head )
> +    {
> +      struct gc_arena gc = gc_new ();
> 
> That gc is never gc'ed and is alos not used :)

Well spotted.  That's what you get for starting with a copy of another
function... :)

v2 attached.

Being able to remove "ifconfig-ipv6" from the to-be-pushed option list
was much less fun (due to the way pool and "ifconfig-ipv6-push" interact)
- but it is one of the important parts here.  Like, when we release a 
2.3.9 version which blows up when using IPv6 on XP...

gert
-- 
USENET is *not* the non-clickable part of WWW!
                                                           //www.muc.de/~gert/
Gert Doering - Munich, Germany                             g...@greenie.muc.de
fax: +49-89-35655025                        g...@net.informatik.tu-muenchen.de
From 59d7df554aa120588bca4f89869c6cbcf42cb4f5 Mon Sep 17 00:00:00 2001
From: Gert Doering <g...@greenie.muc.de>
List-Post: openvpn-devel@lists.sourceforge.net
Date: Sat, 26 Dec 2015 23:08:14 +0100
Subject: [PATCH v2] Implement push-remove option to selectively remove pushed
 options.

With this option, the server can remove individual options from the
set pushed to a client (call from --client-config-dir file, or from
--client-connect script or plugin).  Options are removed at parse
time, so it is possible to do stuff like:

  push-remove route-ipv6
  push "route-ipv6 fd00::/8"

to first remove all IPv6 route options set so far, then add something
specific (what "push-reset" does to all the options).

Arguments to push-remove are strncmp()'ed to option string, so partial
matches like

  push-remove route-ipv6 2001:

are possible ("remove all IPv6 routes starting with 2001:").

Implementation of remove_iroutes_from_push_route_list() had to be changed
slightly to stop it from re-enabling all disabled options again.

v2: documentation (Changes.rst, doc/openvpn.8)
    remove surplus gc_arena
    implement filtering of "ifconfig-ipv6"

Trac #29, #614

Signed-off-by: Gert Doering <g...@greenie.muc.de>
---
 Changes.rst           |  4 ++++
 doc/openvpn.8         | 35 ++++++++++++++++++++++++++++++++++-
 src/openvpn/options.c | 10 ++++++++++
 src/openvpn/options.h |  1 +
 src/openvpn/push.c    | 47 ++++++++++++++++++++++++++++++++++++++++-------
 src/openvpn/push.h    |  1 +
 6 files changed, 90 insertions(+), 8 deletions(-)

diff --git a/Changes.rst b/Changes.rst
index c2142fa..b3acf71 100644
--- a/Changes.rst
+++ b/Changes.rst
@@ -5,6 +5,10 @@ Version 2.4.0
 New features
 ------------
 
+push-remove
+    new option to remove options on a per-client basis from the "push" list
+    (more fine-grained than "push-reset")
+
 keying-material-exporter
     Keying Material Exporter [RFC-5705] allow additional keying material to be
     derived from existing TLS channel.
diff --git a/doc/openvpn.8 b/doc/openvpn.8
index 368bd4c..ed0a1a4 100644
--- a/doc/openvpn.8
+++ b/doc/openvpn.8
@@ -2964,6 +2964,39 @@ as with a
 configuration file.  This option will ignore
 .B \-\-push
 options at the global config file level.
+.\"*********************************************************
+.TP
+.B \-\-push\-remove opt
+selectively remove all
+.B \-\-push
+options matching "opt" from the option list for a client.  "opt" is matched
+as a substring against the whole option string to-be-pushed to the client, so
+.B \-\-push\-remove route
+would remove all
+.B \-\-push route ...
+and
+.B \-\-push route-ipv6 ...
+statements, while
+.B \-\-push\-remove 'route-ipv6 2001:'
+would only remove IPv6 routes for 2001:... networks.
+
+.B \-\-push\-remove
+can only be used in a client-specific context, like in a
+.B \-\-client\-config\-dir
+file, or
+.B \-\-client\-connect
+script or plugin -- similar to
+.B \-\-push\-reset,
+just more selective.
+
+NOTE: to
+.I change
+an option,
+.B \-\-push\-remove
+can be used to first remove the old value, and then add a new
+.B \-\-push
+option with the new value.
+.\"*********************************************************
 .TP
 .B \-\-push\-peer\-info
 Push additional information about the client to server.  The additional information
@@ -3288,7 +3321,7 @@ without needing to restart the server.
 
 The following
 options are legal in a client-specific context:
-.B \-\-push, \-\-push\-reset, \-\-iroute, \-\-ifconfig\-push,
+.B \-\-push, \-\-push\-reset, \-\-push\-remove, \-\-iroute, \-\-ifconfig\-push,
 and
 .B \-\-config.
 .\"*********************************************************
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 46aa824..bf1d2d7 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -5578,6 +5578,15 @@ add_option (struct options *options,
       VERIFY_PERMISSION (OPT_P_INSTANCE);
       push_reset (options);
     }
+  else if (streq (p[0], "push-remove"))
+    {
+      int j;
+      VERIFY_PERMISSION (OPT_P_INSTANCE);
+      for (j = 1; j < MAX_PARMS && p[j]; ++j)
+        {
+	  push_remove_option (options,p[j]);
+	}
+    }
   else if (streq (p[0], "ifconfig-pool") && p[1] && p[2] && !p[4])
     {
       const int lev = M_WARN;
@@ -5926,6 +5935,7 @@ add_option (struct options *options,
       options->push_ifconfig_ipv6_local = local;
       options->push_ifconfig_ipv6_netbits = netbits;
       options->push_ifconfig_ipv6_remote = remote;
+      options->push_ifconfig_ipv6_blocked = false;
     }
   else if (streq (p[0], "disable") && !p[1])
     {
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index 2f6c8b4..7551f79 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -432,6 +432,7 @@ struct options
   struct in6_addr push_ifconfig_ipv6_local;		/* IPv6 */
   int 		  push_ifconfig_ipv6_netbits;		/* IPv6 */
   struct in6_addr push_ifconfig_ipv6_remote;		/* IPv6 */
+  bool            push_ifconfig_ipv6_blocked;		/* IPv6 */
   bool enable_c2c;
   bool duplicate_cn;
   int cf_max;
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index d4f3cb6..c985163 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -255,7 +255,8 @@ send_push_reply (struct context *c)
 
   buf_printf (&buf, "%s", cmd);
 
-  if ( c->c2.push_ifconfig_ipv6_defined )
+  if ( c->c2.push_ifconfig_ipv6_defined &&
+          !c->options.push_ifconfig_ipv6_blocked )
     {
       /* IPv6 is put into buffer first, could be lengthy */
       buf_printf( &buf, ",ifconfig-ipv6 %s/%d %s",
@@ -414,6 +415,37 @@ push_reset (struct options *o)
 {
   CLEAR (o->push_list);
 }
+
+void
+push_remove_option (struct options *o, const char *p)
+{
+  msg( D_PUSH, "PUSH_REMOVE '%s'", p );
+
+  /* ifconfig-ipv6 is special, as not part of the push list */
+  if ( streq( p, "ifconfig-ipv6" ))
+    {
+      o->push_ifconfig_ipv6_blocked = true;
+      return;
+    }
+
+  if (o && o->push_list.head )
+    {
+      struct push_entry *e = o->push_list.head;
+
+      /* cycle through the push list */
+      while (e)
+	{
+	  if ( e->enable &&
+               strncmp( e->option, p, strlen(p) ) == 0 )
+	    {
+	      msg (D_PUSH, "PUSH_REMOVE removing: '%s'", e->option);
+	      e->enable = false;
+	    }
+
+	  e = e->next;
+	}
+    }
+}
 #endif
 
 #if P2MP_SERVER
@@ -543,7 +575,8 @@ remove_iroutes_from_push_route_list (struct options *o)
 
 	  /* parse the push item */
 	  CLEAR (p);
-	  if (parse_line (e->option, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc))
+	  if ( e->enable &&
+               parse_line (e->option, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc))
 	    {
 	      /* is the push item a route directive? */
 	      if (p[0] && !strcmp (p[0], "route") && !p[3])
@@ -569,12 +602,12 @@ remove_iroutes_from_push_route_list (struct options *o)
 			}
 		    }
 		}
-	    }
 
-	  /* should we copy the push item? */
-	  e->enable = enable;
-	  if (!enable)
-	    msg (D_PUSH, "REMOVE PUSH ROUTE: '%s'", e->option);
+	      /* should we copy the push item? */
+	      e->enable = enable;
+	      if (!enable)
+		msg (D_PUSH, "REMOVE PUSH ROUTE: '%s'", e->option);
+	    }
 
 	  e = e->next;
 	}
diff --git a/src/openvpn/push.h b/src/openvpn/push.h
index fa06e08..e756582 100644
--- a/src/openvpn/push.h
+++ b/src/openvpn/push.h
@@ -61,6 +61,7 @@ void push_option (struct options *o, const char *opt, int msglevel);
 void push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc);
 
 void push_reset (struct options *o);
+void push_remove_option (struct options *o, const char *p);
 
 bool send_push_reply (struct context *c);
 
-- 
2.4.10

Attachment: signature.asc
Description: PGP signature

Reply via email to