[Openvpn-devel,v3,01/13] client-connect: Split multi_connection_established into separate functions

Message ID 20181112115627.5096-2-arne@rfc2549.org
State Superseded
Headers show
Series Deferred client-connect patch set | expand

Commit Message

Arne Schwabe Nov. 12, 2018, 12:56 a.m. UTC
From: Fabian Knittel <fabian.knittel@lettink.de>

This patch splits up the multi_connection_established() function.  Each new
helper function does a specific job.  Functions that do a similar job receive a
similar calling interface.

The patch tries not to reindent code, so that the real changes are as clearly
visible as possible.  (A follow-up patch will only do indentation changes.)

Signed-off-by: Fabian Knittel <fabian.knittel@lettink.de>

PATCH v3: Since the code has changed enough from the time the original patch
to the current master, the splitting has been redone from the current code.
Also some style and minor code changes have been added doing this patch.
This elimininates and the big reformatting done before eliminates the follow
up patch with indentation changes.

The original patch already replaces some instances of option_permission_mask
with CLIENT_CONNECT_OPT_MASK. The V3 version does this more consequently.

Signed-off-by: Arne Schwabe <arne@rfc2549.org>
---
 src/openvpn/multi.c | 598 +++++++++++++++++++++++++-------------------
 src/openvpn/multi.h |   4 +-
 2 files changed, 347 insertions(+), 255 deletions(-)

Patch

diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 8440f311..13106fcc 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1638,7 +1638,6 @@  static void
 multi_client_connect_post(struct multi_context *m,
                           struct multi_instance *mi,
                           const char *dc_file,
-                          unsigned int option_permissions_mask,
                           unsigned int *option_types_found)
 {
     /* Did script generate a dynamic config file? */
@@ -1647,7 +1646,7 @@  multi_client_connect_post(struct multi_context *m,
         options_server_import(&mi->context.options,
                               dc_file,
                               D_IMPORT_ERRORS|M_OPTERR,
-                              option_permissions_mask,
+                              CLIENT_CONNECT_OPT_MASK,
                               option_types_found,
                               mi->context.c2.es);
 
@@ -1671,7 +1670,6 @@  static void
 multi_client_connect_post_plugin(struct multi_context *m,
                                  struct multi_instance *mi,
                                  const struct plugin_return *pr,
-                                 unsigned int option_permissions_mask,
                                  unsigned int *option_types_found)
 {
     struct plugin_return config;
@@ -1689,7 +1687,7 @@  multi_client_connect_post_plugin(struct multi_context *m,
                 options_string_import(&mi->context.options,
                                       config.list[i]->value,
                                       D_IMPORT_ERRORS|M_OPTERR,
-                                      option_permissions_mask,
+                                      CLIENT_CONNECT_OPT_MASK,
                                       option_types_found,
                                       mi->context.c2.es);
             }
@@ -1716,21 +1714,19 @@  multi_client_connect_post_plugin(struct multi_context *m,
 static void
 multi_client_connect_mda(struct multi_context *m,
                          struct multi_instance *mi,
-                         const struct buffer_list *config,
-                         unsigned int option_permissions_mask,
                          unsigned int *option_types_found)
 {
-    if (config)
+    if (mi->cc_config)
     {
         struct buffer_entry *be;
 
-        for (be = config->head; be != NULL; be = be->next)
+        for (be = mi->cc_config->head; be != NULL; be = be->next)
         {
             const char *opt = BSTR(&be->buf);
             options_string_import(&mi->context.options,
                                   opt,
                                   D_IMPORT_ERRORS|M_OPTERR,
-                                  option_permissions_mask,
+                                  CLIENT_CONNECT_OPT_MASK,
                                   option_types_found,
                                   mi->context.c2.es);
         }
@@ -1773,215 +1769,387 @@  multi_client_connect_setenv(struct multi_context *m,
     gc_free(&gc);
 }
 
-/*
- * Called as soon as the SSL/TLS connection authenticates.
- *
- * Instance-specific directives to be processed:
- *
- *   iroute start-ip end-ip
- *   ifconfig-push local remote-netmask
- *   push
- */
 static void
-multi_connection_established(struct multi_context *m, struct multi_instance *mi)
+multi_client_connect_call_plugin_v1(struct multi_context *m,
+                                    struct multi_instance *mi,
+                                    unsigned int *option_types_found,
+                                    int *cc_succeeded,
+                                    int *cc_succeeded_count)
 {
-    if (tls_authentication_status(mi->context.c2.tls_multi, 0) == TLS_AUTHENTICATION_SUCCEEDED)
+#ifdef ENABLE_PLUGIN
+    ASSERT(m);
+    ASSERT(mi);
+    ASSERT(option_types_found);
+    ASSERT(cc_succeeded);
+    ASSERT(cc_succeeded_count);
+
+    /* deprecated callback, use a file for passing back return info */
+    if (plugin_defined(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT))
     {
+        struct argv argv = argv_new();
         struct gc_arena gc = gc_new();
-        unsigned int option_types_found = 0;
+        const char *dc_file =
+            platform_create_temp_file(mi->context.options.tmp_dir, "cc", &gc);
 
-        const unsigned int option_permissions_mask =
-            OPT_P_INSTANCE
-            | OPT_P_INHERIT
-            | OPT_P_PUSH
-            | OPT_P_TIMER
-            | OPT_P_CONFIG
-            | OPT_P_ECHO
-            | OPT_P_COMP
-            | OPT_P_SOCKFLAGS;
+        if (!dc_file)
+        {
+            cc_succeeded = false;
+            goto cleanup;
+        }
 
-        int cc_succeeded = true; /* client connect script status */
-        int cc_succeeded_count = 0;
+        argv_printf(&argv, "%s", dc_file);
+        if (plugin_call(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT,
+                        &argv, NULL, mi->context.c2.es)
+            != OPENVPN_PLUGIN_FUNC_SUCCESS)
+        {
+            msg(M_WARN, "WARNING: client-connect plugin call failed");
+            *cc_succeeded = false;
+        }
+        else
+        {
+            multi_client_connect_post(m, mi, dc_file, option_types_found);
+            (*cc_succeeded_count)++;
+        }
+
+        if (!platform_unlink(dc_file))
+        {
+            msg(D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s",
+                dc_file);
+        }
+
+    cleanup:
+        argv_reset(&argv);
+        gc_free(&gc);
+    }
+#endif /* ifdef ENABLE_PLUGIN */
+}
 
-        ASSERT(mi->context.c1.tuntap);
+static void
+multi_client_connect_call_plugin_v2(struct multi_context *m,
+                                    struct multi_instance *mi,
+                                    unsigned int *option_types_found,
+                                    int *cc_succeeded,
+                                    int *cc_succeeded_count)
+{
+#ifdef ENABLE_PLUGIN
+    ASSERT(m);
+    ASSERT(mi);
+    ASSERT(option_types_found);
+    ASSERT(cc_succeeded);
+    ASSERT(cc_succeeded_count);
 
-        /* lock down the common name and cert hashes so they can't change during future TLS renegotiations */
-        tls_lock_common_name(mi->context.c2.tls_multi);
-        tls_lock_cert_hash_set(mi->context.c2.tls_multi);
+    /* V2 callback, use a plugin_return struct for passing back return info */
+    if (plugin_defined(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2))
+    {
+        struct plugin_return pr;
 
-        /* generate a msg() prefix for this client instance */
-        generate_prefix(mi);
+        plugin_return_init(&pr);
 
-        /* delete instances of previous clients with same common-name */
-        if (!mi->context.options.duplicate_cn)
+        if (plugin_call(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2,
+                        NULL, &pr, mi->context.c2.es)
+            != OPENVPN_PLUGIN_FUNC_SUCCESS)
         {
-            multi_delete_dup(m, mi);
+            msg(M_WARN, "WARNING: client-connect-v2 plugin call failed");
+            *cc_succeeded = false;
+        }
+        else
+        {
+            multi_client_connect_post_plugin(m, mi, &pr, option_types_found);
+            (*cc_succeeded_count)++;
         }
 
-        /* reset pool handle to null */
-        mi->vaddr_handle = -1;
+        plugin_return_free(&pr);
+    }
+#endif /* ifdef ENABLE_PLUGIN */
+}
 
-        /*
-         * Try to source a dynamic config file from the
-         * --client-config-dir directory.
-         */
-        if (mi->context.options.client_config_dir)
+
+
+/**
+ * Runs the --client-connect script if one is defined.
+ */
+static void
+multi_client_connect_call_script(struct multi_context *m,
+                                 struct multi_instance *mi,
+                                 unsigned int *option_types_found,
+                                 int *cc_succeeded,
+                                 int *cc_succeeded_count)
+{
+    if (mi->context.options.client_connect_script)
+    {
+        struct argv argv = argv_new();
+        struct gc_arena gc = gc_new();
+        const char *dc_file = NULL;
+
+        setenv_str(mi->context.c2.es, "script_type", "client-connect");
+
+        dc_file = platform_create_temp_file(mi->context.options.tmp_dir,
+                                            "cc", &gc);
+        if (!dc_file)
         {
-            const char *ccd_file;
+            *cc_succeeded = false;
+            goto cleanup;
+        }
 
-            ccd_file = platform_gen_path(mi->context.options.client_config_dir,
-                                         tls_common_name(mi->context.c2.tls_multi,
-                                                         false),
-                                         &gc);
+        argv_parse_cmd(&argv, mi->context.options.client_connect_script);
+        argv_printf_cat(&argv, "%s", dc_file);
 
-            /* try common-name file */
-            if (platform_test_file(ccd_file))
-            {
-                options_server_import(&mi->context.options,
-                                      ccd_file,
-                                      D_IMPORT_ERRORS|M_OPTERR,
-                                      option_permissions_mask,
-                                      &option_types_found,
-                                      mi->context.c2.es);
-            }
-            else /* try default file */
-            {
-                ccd_file = platform_gen_path(mi->context.options.client_config_dir,
-                                             CCD_DEFAULT,
-                                             &gc);
+        if (openvpn_run_script(&argv, mi->context.c2.es, 0, "--client-connect"))
+        {
+            multi_client_connect_post(m, mi, dc_file, option_types_found);
+            (*cc_succeeded_count)++;
+        }
+        else
+        {
+            *cc_succeeded = false;
+        }
 
-                if (platform_test_file(ccd_file))
-                {
-                    options_server_import(&mi->context.options,
-                                          ccd_file,
-                                          D_IMPORT_ERRORS|M_OPTERR,
-                                          option_permissions_mask,
-                                          &option_types_found,
-                                          mi->context.c2.es);
-                }
-            }
+        if (!platform_unlink(dc_file))
+        {
+            msg(D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s",
+                dc_file);
         }
+    cleanup:
+        argv_reset(&argv);
+        gc_free(&gc);
+    }
+}
 
-        /*
-         * Select a virtual address from either --ifconfig-push in --client-config-dir file
-         * or --ifconfig-pool.
-         */
-        multi_select_virtual_addr(m, mi);
+static void
+multi_client_connect_late_setup(struct multi_context *m,
+                                struct multi_instance *mi,
+                                const unsigned int option_types_found)
+{
+    struct gc_arena gc = gc_new();
+    /*
+     * Process sourced options.
+     */
+    do_deferred_options(&mi->context, option_types_found);
 
-        /* do --client-connect setenvs */
-        multi_client_connect_setenv(m, mi);
+    /*
+     * make sure we got ifconfig settings from somewhere
+     */
+    if (!mi->context.c2.push_ifconfig_defined)
+    {
+        msg(D_MULTI_ERRORS, "MULTI: no dynamic or static remote"
+            "--ifconfig address is available for %s",
+            multi_instance_string(mi, false, &gc));
+    }
+
+    /*
+     * make sure that ifconfig settings comply with constraints
+     */
+    if (!ifconfig_push_constraint_satisfied(&mi->context))
+    {
+        const char* ifconfig_constraint_network =
+            print_in_addr_t(mi->context.options.push_ifconfig_constraint_network, 0, &gc);
+        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)",
+            multi_instance_string(mi, false, &gc),
+            print_in_addr_t(mi->context.c2.push_ifconfig_local, 0, &gc),
+            ifconfig_constraint_network, ifconfig_constraint_netmask);
+    }
+
+    /*
+     * For routed tunnels, set up internal route to endpoint
+     * plus add all iroute routes.
+     */
+    if (TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN)
+    {
+        if (mi->context.c2.push_ifconfig_defined)
+        {
+            multi_learn_in_addr_t(m, mi,
+                                  mi->context.c2.push_ifconfig_local,
+                                  -1, true);
+            msg(D_MULTI_LOW, "MULTI: primary virtual IP for %s: %s",
+                multi_instance_string(mi, false, &gc),
+                print_in_addr_t(mi->context.c2.push_ifconfig_local, 0, &gc));
+        }
+
+        if (mi->context.c2.push_ifconfig_ipv6_defined)
+        {
+            multi_learn_in6_addr(m, mi,
+                                 mi->context.c2.push_ifconfig_ipv6_local,
+                                 -1, true);
+            /* TODO: find out where addresses are "unlearned"!! */
+            const char* ifconfig_local_ipv6 =
+                print_in6_addr(mi->context.c2.push_ifconfig_ipv6_local, 0, &gc);
+            msg(D_MULTI_LOW, "MULTI: primary virtual IPv6 for %s: %s",
+                multi_instance_string(mi, false, &gc),
+                ifconfig_local_ipv6);
+        }
+
+        /* add routes locally, pointing to new client, if
+         * --iroute options have been specified */
+        multi_add_iroutes(m, mi);
 
-#ifdef ENABLE_PLUGIN
         /*
-         * Call client-connect plug-in.
+         * iroutes represent subnets which are "owned" by a particular
+         * client.  Therefore, do not actually push a route to a client
+         * if it matches one of the client's iroutes.
          */
+        remove_iroutes_from_push_route_list(&mi->context.options);
+    }
+    else if (mi->context.options.iroutes)
+    {
+        msg(D_MULTI_ERRORS, "MULTI: --iroute options rejected for %s -- iroute"
+            "only works with tun-style tunnels",
+            multi_instance_string(mi, false, &gc));
+    }
 
-        /* deprecated callback, use a file for passing back return info */
-        if (plugin_defined(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT))
-        {
-            struct argv argv = argv_new();
-            const char *dc_file = platform_create_temp_file(mi->context.options.tmp_dir,
-                                                            "cc", &gc);
+    /* set our client's VPN endpoint for status reporting purposes */
+    mi->reporting_addr = mi->context.c2.push_ifconfig_local;
+    mi->reporting_addr_ipv6 = mi->context.c2.push_ifconfig_ipv6_local;
 
-            if (!dc_file)
-            {
-                cc_succeeded = false;
-                goto script_depr_failed;
-            }
+    /* set context-level authentication flag */
+    mi->context.c2.context_auth = CAS_SUCCEEDED;
 
-            argv_printf(&argv, "%s", dc_file);
-            if (plugin_call(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, &argv, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
-            {
-                msg(M_WARN, "WARNING: client-connect plugin call failed");
-                cc_succeeded = false;
-            }
-            else
-            {
-                multi_client_connect_post(m, mi, dc_file, option_permissions_mask, &option_types_found);
-                ++cc_succeeded_count;
-            }
+#ifdef ENABLE_ASYNC_PUSH
+    /* authentication complete, send push reply */
+    if (mi->context.c2.push_request_received)
+    {
+        process_incoming_push_request(&mi->context);
+    }
+#endif
+    gc_free(&gc);
+}
 
-            if (!platform_unlink(dc_file))
-            {
-                msg(D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s",
-                    dc_file);
-            }
 
-script_depr_failed:
-            argv_reset(&argv);
-        }
+static void
+multi_client_connect_early_setup(struct multi_context *m,
+                                 struct multi_instance *mi)
+{
+    ASSERT(mi->context.c1.tuntap);
+    /*
+     * lock down the common name and cert hashes so they can't change
+     * during future TLS renegotiations
+     */
+    tls_lock_common_name(mi->context.c2.tls_multi);
+    tls_lock_cert_hash_set(mi->context.c2.tls_multi);
 
-        /* V2 callback, use a plugin_return struct for passing back return info */
-        if (plugin_defined(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2))
-        {
-            struct plugin_return pr;
+    /* generate a msg() prefix for this client instance */
+    generate_prefix(mi);
 
-            plugin_return_init(&pr);
+    /* delete instances of previous clients with same common-name */
+    if (!mi->context.options.duplicate_cn)
+    {
+        multi_delete_dup(m, mi);
+    }
 
-            if (plugin_call(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2, NULL, &pr, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
-            {
-                msg(M_WARN, "WARNING: client-connect-v2 plugin call failed");
-                cc_succeeded = false;
-            }
-            else
-            {
-                multi_client_connect_post_plugin(m, mi, &pr, option_permissions_mask, &option_types_found);
-                ++cc_succeeded_count;
-            }
+    /* reset pool handle to null */
+    mi->vaddr_handle = -1;
+}
 
-            plugin_return_free(&pr);
-        }
-#endif /* ifdef ENABLE_PLUGIN */
+/**
+ * Try to source a dynamic config file from the
+ * --client-config-dir directory.
+ */
+static void
+multi_client_connect_source_ccd(struct multi_context *m,
+                                struct multi_instance *mi,
+                                unsigned int *option_types_found)
+{
+    if (mi->context.options.client_config_dir)
+    {
+        struct gc_arena gc = gc_new();
+        const char *ccd_file;
 
-        /*
-         * Run --client-connect script.
-         */
-        if (mi->context.options.client_connect_script && cc_succeeded)
-        {
-            struct argv argv = argv_new();
-            const char *dc_file = NULL;
+        ccd_file = platform_gen_path(mi->context.options.client_config_dir,
+                                     tls_common_name(mi->context.c2.tls_multi,
+                                                     false),
+                                     &gc);
 
-            setenv_str(mi->context.c2.es, "script_type", "client-connect");
+        /* try common-name file */
+        if (platform_test_file(ccd_file))
+        {
+            options_server_import(&mi->context.options,
+                                  ccd_file,
+                                  D_IMPORT_ERRORS|M_OPTERR,
+                                  CLIENT_CONNECT_OPT_MASK,
+                                  option_types_found,
+                                  mi->context.c2.es);
+        }
+        else /* try default file */
+        {
+            ccd_file = platform_gen_path(mi->context.options.client_config_dir,
+                                         CCD_DEFAULT,
+                                         &gc);
 
-            dc_file = platform_create_temp_file(mi->context.options.tmp_dir,
-                                                "cc", &gc);
-            if (!dc_file)
+            if (platform_test_file(ccd_file))
             {
-                cc_succeeded = false;
-                goto script_failed;
+                options_server_import(&mi->context.options,
+                                      ccd_file,
+                                      D_IMPORT_ERRORS|M_OPTERR,
+                                      CLIENT_CONNECT_OPT_MASK,
+                                      option_types_found,
+                                      mi->context.c2.es);
             }
+        }
+        gc_free(&gc);
+    }
+}
+
+/*
+ * Called as soon as the SSL/TLS connection authenticates.
+ *
+ * Instance-specific directives to be processed:
+ *
+ *   iroute start-ip end-ip
+ *   ifconfig-push local remote-netmask
+ *   push
+ */
+static void
+multi_connection_established(struct multi_context *m, struct multi_instance *mi)
+{
+    if (tls_authentication_status(mi->context.c2.tls_multi, 0)
+        == TLS_AUTHENTICATION_SUCCEEDED)
+    {
+        unsigned int option_types_found = 0;
 
-            argv_parse_cmd(&argv, mi->context.options.client_connect_script);
-            argv_printf_cat(&argv, "%s", dc_file);
+        int cc_succeeded = true; /* client connect script status */
+        int cc_succeeded_count = 0;
 
-            if (openvpn_run_script(&argv, mi->context.c2.es, 0, "--client-connect"))
-            {
-                multi_client_connect_post(m, mi, dc_file, option_permissions_mask, &option_types_found);
-                ++cc_succeeded_count;
-            }
-            else
-            {
-                cc_succeeded = false;
-            }
+        multi_client_connect_early_setup (m, mi);
 
-            if (!platform_unlink(dc_file))
-            {
-                msg(D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s",
-                    dc_file);
-            }
+        multi_client_connect_source_ccd (m, mi, &option_types_found);
 
-script_failed:
-            argv_reset(&argv);
-        }
+        /*
+         * Select a virtual address from either --ifconfig-push in
+         * --client-config-dir file or --ifconfig-pool.
+         */
+        multi_select_virtual_addr(m, mi);
+
+        /* do --client-connect setenvs */
+        multi_client_connect_setenv(m, mi);
+
+        multi_client_connect_call_plugin_v1 (m, mi, &option_types_found,
+                                             &cc_succeeded,
+                                             &cc_succeeded_count);
+
+        multi_client_connect_call_plugin_v2 (m, mi, &option_types_found,
+                                             &cc_succeeded,
+                                             &cc_succeeded_count);
 
         /*
          * Check for client-connect script left by management interface client
          */
+
+        if (cc_succeeded)
+        {
+            multi_client_connect_call_script (m, mi, &option_types_found,
+                                              &cc_succeeded,
+                                              &cc_succeeded_count);
+
+        }
 #ifdef MANAGEMENT_DEF_AUTH
         if (cc_succeeded && mi->cc_config)
         {
-            multi_client_connect_mda(m, mi, mi->cc_config, option_permissions_mask, &option_types_found);
-            ++cc_succeeded_count;
+            multi_client_connect_mda(m, mi, &option_types_found);
+            cc_succeeded_count++;
         }
 #endif
 
@@ -1991,99 +2159,21 @@  script_failed:
          */
         if (mi->context.options.disable)
         {
-            msg(D_MULTI_ERRORS, "MULTI: client has been rejected due to 'disable' directive");
+            msg(D_MULTI_ERRORS, "MULTI: client has been rejected due to"
+                "'disable' directive");
             cc_succeeded = false;
             cc_succeeded_count = 0;
         }
 
         if (cc_succeeded)
         {
-            /*
-             * Process sourced options.
-             */
-            do_deferred_options(&mi->context, option_types_found);
-
-            /*
-             * make sure we got ifconfig settings from somewhere
-             */
-            if (!mi->context.c2.push_ifconfig_defined)
-            {
-                msg(D_MULTI_ERRORS, "MULTI: no dynamic or static remote --ifconfig address is available for %s",
-                    multi_instance_string(mi, false, &gc));
-            }
-
-            /*
-             * make sure that ifconfig settings comply with constraints
-             */
-            if (!ifconfig_push_constraint_satisfied(&mi->context))
-            {
-                /* 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)",
-                    multi_instance_string(mi, false, &gc),
-                    print_in_addr_t(mi->context.c2.push_ifconfig_local, 0, &gc),
-                    print_in_addr_t(mi->context.options.push_ifconfig_constraint_network, 0, &gc),
-                    print_in_addr_t(mi->context.options.push_ifconfig_constraint_netmask, 0, &gc));
-            }
-
-            /*
-             * For routed tunnels, set up internal route to endpoint
-             * plus add all iroute routes.
-             */
-            if (TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN)
-            {
-                if (mi->context.c2.push_ifconfig_defined)
-                {
-                    multi_learn_in_addr_t(m, mi, mi->context.c2.push_ifconfig_local, -1, true);
-                    msg(D_MULTI_LOW, "MULTI: primary virtual IP for %s: %s",
-                        multi_instance_string(mi, false, &gc),
-                        print_in_addr_t(mi->context.c2.push_ifconfig_local, 0, &gc));
-                }
-
-                if (mi->context.c2.push_ifconfig_ipv6_defined)
-                {
-                    multi_learn_in6_addr(m, mi, mi->context.c2.push_ifconfig_ipv6_local, -1, true);
-                    /* TODO: find out where addresses are "unlearned"!! */
-                    msg(D_MULTI_LOW, "MULTI: primary virtual IPv6 for %s: %s",
-                        multi_instance_string(mi, false, &gc),
-                        print_in6_addr(mi->context.c2.push_ifconfig_ipv6_local, 0, &gc));
-                }
-
-                /* add routes locally, pointing to new client, if
-                 * --iroute options have been specified */
-                multi_add_iroutes(m, mi);
-
-                /*
-                 * iroutes represent subnets which are "owned" by a particular
-                 * client.  Therefore, do not actually push a route to a client
-                 * if it matches one of the client's iroutes.
-                 */
-                remove_iroutes_from_push_route_list(&mi->context.options);
-            }
-            else if (mi->context.options.iroutes)
-            {
-                msg(D_MULTI_ERRORS, "MULTI: --iroute options rejected for %s -- iroute only works with tun-style tunnels",
-                    multi_instance_string(mi, false, &gc));
-            }
-
-            /* set our client's VPN endpoint for status reporting purposes */
-            mi->reporting_addr = mi->context.c2.push_ifconfig_local;
-            mi->reporting_addr_ipv6 = mi->context.c2.push_ifconfig_ipv6_local;
-
-            /* set context-level authentication flag */
-            mi->context.c2.context_auth = CAS_SUCCEEDED;
-
-#ifdef ENABLE_ASYNC_PUSH
-            /* authentication complete, send push reply */
-            if (mi->context.c2.push_request_received)
-            {
-                process_incoming_push_request(&mi->context);
-            }
-#endif
+            multi_client_connect_late_setup (m, mi, option_types_found);
         }
         else
         {
             /* set context-level authentication flag */
-            mi->context.c2.context_auth = cc_succeeded_count ? CAS_PARTIAL : CAS_FAILED;
+            mi->context.c2.context_auth =
+                cc_succeeded_count ? CAS_PARTIAL : CAS_FAILED;
         }
 
         /* set flag so we don't get called again */
@@ -2097,11 +2187,11 @@  script_failed:
 #ifdef MANAGEMENT_DEF_AUTH
         if (management)
         {
-            management_connection_established(management, &mi->context.c2.mda_context, mi->context.c2.es);
+            management_connection_established
+                (management, &mi->context.c2.mda_context, mi->context.c2.es);
         }
 #endif
 
-        gc_free(&gc);
     }
 
     /*
diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h
index 3d3d6875..2e20d152 100644
--- a/src/openvpn/multi.h
+++ b/src/openvpn/multi.h
@@ -628,7 +628,9 @@  multi_process_outgoing_tun(struct multi_context *m, const unsigned int mpp_flags
     return ret;
 }
 
-
+#define CLIENT_CONNECT_OPT_MASK (OPT_P_INSTANCE | OPT_P_INHERIT | \
+                                 OPT_P_PUSH | OPT_P_TIMER | OPT_P_CONFIG | \
+                                 OPT_P_ECHO | OPT_P_COMP | OPT_P_SOCKFLAGS)
 
 static inline bool
 multi_process_outgoing_link_dowork(struct multi_context *m, struct multi_instance *mi, const unsigned int mpp_flags)