Attention is currently required from: flichtenheld, ordex, plaisthos.

Hello flichtenheld, plaisthos,

I'd like you to reexamine a change. Please visit

    http://gerrit.openvpn.net/c/openvpn/+/1783?usp=email

to look at the new patch set (#3).


Change subject: multi: assign clients from a named, tagged subnet-pool
......................................................................

multi: assign clients from a named, tagged subnet-pool

Add two server directives:

  subnet-pool <tag> <network> <netmask> [<gateway>]   (main config)
  subnet-pool-tag <tag>                               (per-client CCD)

so groups of clients can be given addresses from subnets other than the
--server network (per-group firewall rules under --topology subnet, and
thus DCO). A client's subnet-pool-tag is resolved to its pool and served
a virtual IP from that subnet: a static --ifconfig-push within it, or the
next free address from a dynamic per-group pool shared by tag and created
on first use. Such a client is validated against its own subnet instead
of the global tunnel-network constraint and never falls back to the
global pool; a tag matching no configured pool is declined with
AUTH_FAILED.

Change-Id: Ia1e7db95615d99ef9687de7c4e996dc8033e2631
GitHub: closes openvpn/OpenVPN#987
Signed-off-by: Antonio Quartulli <[email protected]>
---
M CMakeLists.txt
M doc/man-sections/server-options.rst
M src/openvpn/multi.c
M src/openvpn/multi.h
M src/openvpn/options.c
M src/openvpn/options.h
M src/openvpn/options_util.c
M src/openvpn/options_util.h
M tests/unit_tests/openvpn/Makefile.am
A tests/unit_tests/openvpn/test_subnet_pool.c
10 files changed, 323 insertions(+), 5 deletions(-)


  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/83/1783/3

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 23541a5d..3463b5a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -671,6 +671,7 @@
         "test_provider"
         "test_socket"
         "test_ssl"
+        "test_subnet_pool"
         "test_user_pass"
         "test_push_update_msg"
         )
@@ -829,6 +830,11 @@
         src/openvpn/list.c
         )

+    target_sources(test_subnet_pool PRIVATE
+        tests/unit_tests/openvpn/mock_get_random.c
+        src/openvpn/options_util.c
+        )
+
     target_sources(test_ncp PRIVATE
         src/openvpn/crypto_epoch.c
         src/openvpn/crypto_mbedtls.c
diff --git a/doc/man-sections/server-options.rst 
b/doc/man-sections/server-options.rst
index f420588..b6464af 100644
--- a/doc/man-sections/server-options.rst
+++ b/doc/man-sections/server-options.rst
@@ -330,6 +330,37 @@
   by ``--ifconfig``, OpenVPN will install a /32 host route for the ``local``
   IP address.

+--subnet-pool args
+  Define a named pool of addresses from a subnet different from the
+  ``--server`` network, so groups of clients can be given IPs from separate
+  subnets for per-group firewall rules under ``--topology subnet`` (and
+  therefore DCO). This is a main-config directive and may be repeated to
+  define several pools.
+
+  Valid syntax:
+  ::
+
+     subnet-pool tag network netmask [gateway]
+
+  ``tag`` is a name a client refers to with ``--subnet-pool-tag``; ``gateway``
+  defaults to the first address of ``network``/``netmask``. A client assigned
+  to a pool receives (dynamically, or via a static ``--ifconfig-push`` that
+  must lie within the subnet) an address from it, plus the matching netmask
+  and a ``--route-gateway`` inside the subnet so that its pushed routes
+  install on-link.
+
+  A host route to the server's own VPN IP is not pushed, as it is not needed
+  to reach the networks behind the server. If your clients need to reach the
+  server's VPN address itself, add an explicit
+  ``push "route <server-VPN-IP> 255.255.255.255"`` (or the ``route-ipv6``
+  equivalent) to the server config.
+
+--subnet-pool-tag tag
+  Assign this client to the ``--subnet-pool`` named ``tag``. Must be
+  associated with a specific client instance via ``--client-config-dir`` or
+  ``--client-connect``. Several clients may reference the same tag and are
+  then served from the same shared pool.
+
 --ifconfig-ipv6-push args
   for ``--client-config-dir`` per-client static IPv6 interface
   configuration, see ``--client-config-dir`` and ``--ifconfig-push`` for
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index c4293a7..da46cf4 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -42,6 +42,7 @@
 #include "vlan.h"
 #include "auth_token.h"
 #include "route.h"
+#include "options_util.h"
 #include <inttypes.h>
 #include <string.h>

@@ -53,6 +54,17 @@
 #include "dco.h"
 #include "reflect_filter.h"

+/*
+ * Dynamic address pool serving the clients of one --subnet-pool tag.  Shared
+ * by every client that references the tag, created on first use.
+ */
+struct subnet_pool
+{
+    struct subnet_pool *next;
+    const char *tag;
+    struct ifconfig_pool *pool;
+};
+
 /*#define MULTI_DEBUG_EVENT_LOOP*/

 #ifdef MULTI_DEBUG_EVENT_LOOP
@@ -612,7 +624,8 @@

         schedule_remove_entry(m->schedule, (struct schedule_entry *)mi);

-        ifconfig_pool_release(m->ifconfig_pool, mi->vaddr_handle, false);
+        ifconfig_pool_release(mi->vaddr_pool ? mi->vaddr_pool : 
m->ifconfig_pool, mi->vaddr_handle,
+                              false);

         if (mi->did_iroutes)
         {
@@ -687,6 +700,14 @@
         schedule_free(m->schedule);
         mbuf_free(m->mbuf);
         ifconfig_pool_free(m->ifconfig_pool);
+        while (m->subnet_pools)
+        {
+            struct subnet_pool *sp = m->subnet_pools;
+
+            m->subnet_pools = sp->next;
+            ifconfig_pool_free(sp->pool);
+            free(sp);
+        }
         frequency_limit_free(m->new_connection_limiter);
         initial_rate_limit_free(m->initial_rate_limiter);
         multi_reap_free(m->reaper);
@@ -1384,6 +1405,12 @@
     const struct options *o = &c->options;
     if (o->push_ifconfig_constraint_defined && c->c2.push_ifconfig_defined)
     {
+        /* a subnet-pool client is validated against its own subnet */
+        if (o->subnet_pool_defined)
+        {
+            return (o->subnet_pool_netmask & c->c2.push_ifconfig_local)
+                   == o->subnet_pool_network;
+        }
         return (o->push_ifconfig_constraint_netmask & 
c->c2.push_ifconfig_local)
                == o->push_ifconfig_constraint_network;
     }
@@ -1394,6 +1421,34 @@
 }

 /*
+ * Return the dynamic pool serving the clients tagged with <tag>, creating it
+ * on first use from the matching --subnet-pool definition.
+ */
+static struct ifconfig_pool *
+multi_get_group_pool(struct multi_context *m, const char *tag)
+{
+    struct subnet_pool *sp;
+
+    for (sp = m->subnet_pools; sp; sp = sp->next)
+    {
+        if (!strcmp(sp->tag, tag))
+        {
+            return sp->pool;
+        }
+    }
+
+    const struct subnet_pool_def *d4 = 
subnet_pool_by_tag(m->top.options.subnet_pools, tag);
+    ALLOC_OBJ_CLEAR(sp, struct subnet_pool);
+    sp->tag = d4->tag;
+    sp->pool = ifconfig_pool_init(true, IFCONFIG_POOL_INDIV, d4->network + 2,
+                                  (d4->network | ~d4->netmask) - 1, 
m->top.options.duplicate_cn,
+                                  false, in6addr_any, 0);
+    sp->next = m->subnet_pools;
+    m->subnet_pools = sp;
+    return sp->pool;
+}
+
+/*
  * Select a virtual address for a new client instance.
  * Use an --ifconfig-push directive, if given (static IP).
  * Otherwise use an --ifconfig-pool address (dynamic IP).
@@ -1403,6 +1458,21 @@
 {
     struct gc_arena gc = gc_new();

+    /* resolve a CCD --subnet-pool-tag against the main-config --subnet-pool 
list
+     * (an unknown tag is rejected with AUTH_FAILED in multi_client_connect) */
+    if (mi->context.options.subnet_pool_tag && 
!mi->context.options.subnet_pool_defined)
+    {
+        const struct subnet_pool_def *d =
+            subnet_pool_by_tag(m->top.options.subnet_pools, 
mi->context.options.subnet_pool_tag);
+        if (d)
+        {
+            mi->context.options.subnet_pool_defined = true;
+            mi->context.options.subnet_pool_network = d->network;
+            mi->context.options.subnet_pool_netmask = d->netmask;
+            mi->context.options.subnet_pool_gateway = d->gateway;
+        }
+    }
+
     /*
      * If ifconfig addresses were set by dynamic config file,
      * release pool addresses, otherwise keep them.
@@ -1413,8 +1483,10 @@
          * release dynamic allocation */
         if (mi->vaddr_handle >= 0)
         {
-            ifconfig_pool_release(m->ifconfig_pool, mi->vaddr_handle, true);
+            ifconfig_pool_release(mi->vaddr_pool ? mi->vaddr_pool : 
m->ifconfig_pool,
+                                  mi->vaddr_handle, true);
             mi->vaddr_handle = -1;
+            mi->vaddr_pool = NULL;
         }

         mi->context.c2.push_ifconfig_defined = true;
@@ -1434,6 +1506,44 @@
                 "MULTI_sva: WARNING: if --ifconfig-push is used for IPv4, 
automatic IPv6 assignment from --ifconfig-ipv6-pool does not work.  Use 
--ifconfig-ipv6-push for IPv6 then.");
         }
     }
+    else if (mi->context.options.subnet_pool_defined)
+    {
+        /* dynamic address from this client's --subnet-pool group */
+
+        /* the CCD is read after a global-pool address may already have been
+         * acquired; drop it so we can serve this client from its group */
+        if (mi->vaddr_handle >= 0 && !mi->vaddr_pool)
+        {
+            ifconfig_pool_release(m->ifconfig_pool, mi->vaddr_handle, true);
+            mi->vaddr_handle = -1;
+        }
+
+        if (mi->vaddr_handle < 0)
+        {
+            in_addr_t local = 0, remote = 0;
+            const char *cn = NULL;
+
+            if (!mi->context.options.duplicate_cn)
+            {
+                cn = tls_common_name(mi->context.c2.tls_multi, true);
+            }
+
+            mi->vaddr_pool = multi_get_group_pool(m, 
mi->context.options.subnet_pool_tag);
+            mi->vaddr_handle = ifconfig_pool_acquire(mi->vaddr_pool, &local, 
&remote, NULL, cn);
+            if (mi->vaddr_handle >= 0)
+            {
+                mi->context.c2.push_ifconfig_local = remote;
+                mi->context.c2.push_ifconfig_remote_netmask =
+                    mi->context.options.subnet_pool_netmask;
+                mi->context.c2.push_ifconfig_defined = true;
+            }
+            else
+            {
+                msg(D_MULTI_ERRORS, "MULTI: no free --subnet-pool addresses 
are available for %s",
+                    multi_instance_string(mi, false, &gc));
+            }
+        }
+    }
     else if (m->ifconfig_pool && mi->vaddr_handle < 0) /* otherwise, choose a 
pool address */
     {
         in_addr_t local = 0, remote = 0;
@@ -2339,10 +2449,9 @@
         const char *ifconfig_constraint_netmask =
             
print_in_addr_t(mi->context.options.push_ifconfig_constraint_netmask, 0, &gc);

-        /* JYFIXME -- this should cause the connection to fail */
         msg(D_MULTI_ERRORS,
-            "MULTI ERROR: primary virtual IP for %s (%s) "
-            "violates tunnel network/netmask constraint (%s/%s)",
+            "WARNING: primary virtual IP for %s (%s) "
+            "violates tunnel network/netmask constraint (%s/%s) and may not 
work",
             multi_instance_string(mi, false, &gc),
             print_in_addr_t(mi->context.c2.push_ifconfig_local, 0, &gc),
             ifconfig_constraint_network, ifconfig_constraint_netmask);
@@ -2450,6 +2559,7 @@

     /* reset pool handle to null */
     mi->vaddr_handle = -1;
+    mi->vaddr_pool = NULL;

     /* do --client-connect setenvs */
     multi_select_virtual_addr(m, mi);
@@ -2741,6 +2851,15 @@
         cc_succeeded = false;
     }

+    if (mi->context.options.subnet_pool_tag
+        && !subnet_pool_by_tag(m->top.options.subnet_pools, 
mi->context.options.subnet_pool_tag))
+    {
+        msg(D_MULTI_ERRORS, "MULTI: client has been rejected due to unknown 
--subnet-pool-tag '%s'",
+            mi->context.options.subnet_pool_tag);
+        auth_set_client_reason(mi->context.c2.tls_multi, "unknown 
subnet-pool-tag");
+        cc_succeeded = false;
+    }
+
     if (cc_succeeded)
     {
         multi_client_connect_late_setup(m, mi, *option_types_found);
diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h
index 8b837fa..f6815f4 100644
--- a/src/openvpn/multi.h
+++ b/src/openvpn/multi.h
@@ -121,6 +121,8 @@
     struct mroute_addr real; /**< External network address of the
                               *   remote peer. */
     ifconfig_pool_handle vaddr_handle;
+    /* pool vaddr_handle belongs to (NULL = the global one) */
+    struct ifconfig_pool *vaddr_pool;
     char msg_prefix[MULTI_PREFIX_MAX_LENGTH];

     /* queued outgoing data in Server/TCP mode */
@@ -176,6 +178,8 @@
                                         *   instances. */
     struct multi_io *multi_io;         /**< I/O state and events tracker */
     struct ifconfig_pool *ifconfig_pool;
+    /* per-group --subnet-pool dynamic pools */
+    struct subnet_pool *subnet_pools;
     struct frequency_limit *new_connection_limiter;
     struct initial_packet_rate_limit *initial_rate_limiter;
     struct mroute_helper *route_helper;
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 87218d4..3f9f7de 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -443,6 +443,11 @@
     "                  If seconds=0, file will be treated as read-only.\n"
     "--ifconfig-ipv6-pool base-IP/bits : set aside an IPv6 network block\n"
     "                  to be dynamically allocated to connecting clients.\n"
+    "--subnet-pool tag network netmask [gateway] : Define a named pool of\n"
+    "                  addresses outside the --server network, selected per\n"
+    "                  client with --subnet-pool-tag.\n"
+    "--subnet-pool-tag tag : Assign this client to the --subnet-pool named 
tag.\n"
+    "                  Only valid in a client-specific config file.\n"
     "--ifconfig-push local remote-netmask : Push an ifconfig option to 
remote,\n"
     "                  overrides --ifconfig-pool dynamic allocation.\n"
     "                  Only valid in a client-specific config file.\n"
@@ -7631,6 +7636,33 @@
             goto err;
         }
     }
+    else if (streq(p[0], "subnet-pool") && p[1] && p[2] && p[3] && !p[5])
+    {
+        struct subnet_pool_def *sp;
+        in_addr_t network, netmask;
+
+        VERIFY_PERMISSION(OPT_P_GENERAL);
+        network = getaddr(GETADDR_HOST_ORDER | GETADDR_RESOLVE, p[2], 0, NULL, 
NULL);
+        netmask = getaddr(GETADDR_HOST_ORDER, p[3], 0, NULL, NULL);
+        if (!network || !netmask)
+        {
+            msg(msglevel, "cannot parse --subnet-pool addresses");
+            goto err;
+        }
+        ALLOC_OBJ_GC(sp, struct subnet_pool_def, &options->gc);
+        sp->tag = p[1];
+        sp->network = network & netmask;
+        sp->netmask = netmask;
+        sp->gateway = p[4] ? getaddr(GETADDR_HOST_ORDER | GETADDR_RESOLVE, 
p[4], 0, NULL, NULL)
+                           : (network & netmask) + 1;
+        sp->next = options->subnet_pools;
+        options->subnet_pools = sp;
+    }
+    else if (streq(p[0], "subnet-pool-tag") && p[1] && !p[2])
+    {
+        VERIFY_PERMISSION(OPT_P_INSTANCE);
+        options->subnet_pool_tag = p[1];
+    }
     else if (streq(p[0], "ifconfig-push-constraint") && p[1] && p[2] && !p[3])
     {
         in_addr_t network, netmask;
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index a111cf8..0c810b3 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -250,6 +250,17 @@
     struct verify_hash_list *next;
 };

+/* A named --subnet-pool: a subnet clients can be assigned from, referenced
+ * from a CCD file by its tag via --subnet-pool-tag. */
+struct subnet_pool_def
+{
+    struct subnet_pool_def *next;
+    const char *tag;
+    in_addr_t network;
+    in_addr_t netmask;
+    in_addr_t gateway;
+};
+
 /* Command line options */
 struct options
 {
@@ -515,6 +526,12 @@
     bool push_ifconfig_constraint_defined;
     in_addr_t push_ifconfig_constraint_network;
     in_addr_t push_ifconfig_constraint_netmask;
+    struct subnet_pool_def *subnet_pools; /* named pools (server config) */
+    const char *subnet_pool_tag;          /* which pool this client uses (CCD) 
*/
+    bool subnet_pool_defined;             /* resolved from the tag at connect 
*/
+    in_addr_t subnet_pool_network;
+    in_addr_t subnet_pool_netmask;
+    in_addr_t subnet_pool_gateway;
     bool push_ifconfig_ipv4_blocked;           /* IPv4 */
     bool push_ifconfig_ipv6_defined;           /* IPv6 */
     struct in6_addr push_ifconfig_ipv6_local;  /* IPv6 */
diff --git a/src/openvpn/options_util.c b/src/openvpn/options_util.c
index 47fe0bc..ba04917 100644
--- a/src/openvpn/options_util.c
+++ b/src/openvpn/options_util.c
@@ -31,6 +31,19 @@

 #include "push.h"

+const struct subnet_pool_def *
+subnet_pool_by_tag(const struct subnet_pool_def *pools, const char *tag)
+{
+    for (const struct subnet_pool_def *d = pools; d; d = d->next)
+    {
+        if (!strcmp(d->tag, tag))
+        {
+            return d;
+        }
+    }
+    return NULL;
+}
+
 const char *
 parse_auth_failed_temp(struct options *o, const char *reason)
 {
diff --git a/src/openvpn/options_util.h b/src/openvpn/options_util.h
index 1d4ea9b..c54357c 100644
--- a/src/openvpn/options_util.h
+++ b/src/openvpn/options_util.h
@@ -28,6 +28,16 @@

 const char *parse_auth_failed_temp(struct options *o, const char *reason);

+/**
+ * Look up a named --subnet-pool in a list of pool definitions.
+ *
+ * @param pools  head of the --subnet-pool list (may be NULL)
+ * @param tag    the --subnet-pool-tag to match
+ * @return       the matching pool definition, or NULL if none matches
+ */
+const struct subnet_pool_def *subnet_pool_by_tag(const struct subnet_pool_def 
*pools,
+                                                 const char *tag);
+

 /** Checks if the string is a valid integer by checking if it can be
  *  converted to an integer */
diff --git a/tests/unit_tests/openvpn/Makefile.am 
b/tests/unit_tests/openvpn/Makefile.am
index 1128eb4..bfb7284 100644
--- a/tests/unit_tests/openvpn/Makefile.am
+++ b/tests/unit_tests/openvpn/Makefile.am
@@ -21,6 +21,7 @@
        push_update_msg_testdriver \
        socket_testdriver \
        ssl_testdriver \
+       subnet_pool_testdriver \
        user_pass_testdriver

 if HAVE_LD_WRAP_SUPPORT
@@ -361,6 +362,19 @@
        $(top_srcdir)/src/openvpn/platform.c \
        $(top_srcdir)/src/openvpn/mbuf.c

+subnet_pool_testdriver_CFLAGS  = \
+       -I$(top_srcdir)/include -I$(top_srcdir)/src/compat 
-I$(top_srcdir)/src/openvpn \
+       @TEST_CFLAGS@
+
+subnet_pool_testdriver_LDFLAGS = @TEST_LDFLAGS@
+
+subnet_pool_testdriver_SOURCES = test_subnet_pool.c \
+       mock_msg.c test_common.h \
+       mock_get_random.c \
+       $(top_srcdir)/src/openvpn/buffer.c \
+       $(top_srcdir)/src/openvpn/options_util.c \
+       $(top_srcdir)/src/openvpn/platform.c
+
 misc_testdriver_CFLAGS  = \
        -I$(top_srcdir)/include -I$(top_srcdir)/src/compat 
-I$(top_srcdir)/src/openvpn \
        -DSOURCEDIR=\"$(top_srcdir)\" @TEST_CFLAGS@
diff --git a/tests/unit_tests/openvpn/test_subnet_pool.c 
b/tests/unit_tests/openvpn/test_subnet_pool.c
new file mode 100644
index 0000000..8e73b64
--- /dev/null
+++ b/tests/unit_tests/openvpn/test_subnet_pool.c
@@ -0,0 +1,72 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2026 OpenVPN Inc <[email protected]>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "syshead.h"
+
+#include <stdlib.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "options.h"
+#include "options_util.h"
+#include "test_common.h"
+#include "mock_msg.h"
+
+/* --subnet-pool-tag resolves a client to its --subnet-pool; an unknown tag
+ * returns NULL, which is what makes the server reject that client. */
+static void
+test_subnet_pool_by_tag(void **state)
+{
+    struct subnet_pool_def c = { .next = NULL, .tag = "grpC", .network = 
0x0a020300, .netmask = 0xffffff00, .gateway = 0x0a020301 };
+    struct subnet_pool_def b = { .next = &c, .tag = "grpB", .network = 
0x0a020200, .netmask = 0xffffff00, .gateway = 0x0a020201 };
+    struct subnet_pool_def a = { .next = &b, .tag = "grpA", .network = 
0x0a020100, .netmask = 0xffffff00, .gateway = 0x0a020101 };
+
+    /* every configured tag resolves to its own pool, wherever it sits */
+    assert_ptr_equal(subnet_pool_by_tag(&a, "grpA"), &a);
+    assert_ptr_equal(subnet_pool_by_tag(&a, "grpB"), &b);
+    assert_ptr_equal(subnet_pool_by_tag(&a, "grpC"), &c);
+
+    /* and it carries that pool's subnet/gateway */
+    const struct subnet_pool_def *r = subnet_pool_by_tag(&a, "grpB");
+    assert_int_equal(r->network, 0x0a020200);
+    assert_int_equal(r->netmask, 0xffffff00);
+    assert_int_equal(r->gateway, 0x0a020201);
+
+    /* unknown tag and empty list both miss */
+    assert_null(subnet_pool_by_tag(&a, "nope"));
+    assert_null(subnet_pool_by_tag(NULL, "grpA"));
+}
+
+const struct CMUnitTest subnet_pool_tests[] = {
+    cmocka_unit_test(test_subnet_pool_by_tag),
+};
+
+int
+main(void)
+{
+    openvpn_unit_test_setup();
+    return cmocka_run_group_tests(subnet_pool_tests, NULL, NULL);
+}

--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/1783?usp=email
To unsubscribe, or for help writing mail filters, visit 
http://gerrit.openvpn.net/settings?usp=email

Gerrit-MessageType: newpatchset
Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: Ia1e7db95615d99ef9687de7c4e996dc8033e2631
Gerrit-Change-Number: 1783
Gerrit-PatchSet: 3
Gerrit-Owner: ordex <[email protected]>
Gerrit-Reviewer: flichtenheld <[email protected]>
Gerrit-Reviewer: plaisthos <[email protected]>
Gerrit-CC: openvpn-devel <[email protected]>
Gerrit-Attention: plaisthos <[email protected]>
Gerrit-Attention: flichtenheld <[email protected]>
Gerrit-Attention: ordex <[email protected]>
_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to