src/openvpn/multi.c
6fbf66fa
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single TCP/UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
49979459
  *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
6fbf66fa
  *
  *  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.
  *
caa54ac3
  *  You should have received a copy of the GNU General Public License along
  *  with this program; if not, write to the Free Software Foundation, Inc.,
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
6fbf66fa
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
0d1a75bf
 #ifdef HAVE_SYS_INOTIFY_H
 #include <sys/inotify.h>
 #define INOTIFY_EVENT_BUFFER_SIZE 16384
 #endif
 
6fbf66fa
 #include "syshead.h"
 
 #if P2MP_SERVER
 
 #include "multi.h"
 #include "push.h"
 #include "misc.h"
 #include "otime.h"
 #include "gremlin.h"
ffea644c
 #include "mstats.h"
63dc03d0
 #include "ssl_verify.h"
65eedc35
 #include <inttypes.h>
6fbf66fa
 
 #include "memdbg.h"
 
 #include "forward-inline.h"
90efcacb
 #include "pf-inline.h"
6fbf66fa
 
8acc40b6
 #include "crypto_backend.h"
 
6fbf66fa
 /*#define MULTI_DEBUG_EVENT_LOOP*/
 
 #ifdef MULTI_DEBUG_EVENT_LOOP
 static const char *
81d882d5
 id(struct multi_instance *mi)
6fbf66fa
 {
81d882d5
     if (mi)
     {
         return tls_common_name(mi->context.c2.tls_multi, false);
     }
     else
     {
         return "NULL";
     }
6fbf66fa
 }
 #endif
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
 static void
81d882d5
 set_cc_config(struct multi_instance *mi, struct buffer_list *cc_config)
90efcacb
 {
81d882d5
     if (mi->cc_config)
     {
         buffer_list_free(mi->cc_config);
     }
     mi->cc_config = cc_config;
90efcacb
 }
 #endif
 
ffea644c
 static inline void
 update_mstat_n_clients(const int n_clients)
 {
 #ifdef ENABLE_MEMSTATS
81d882d5
     if (mmap_stats)
     {
         mmap_stats->n_clients = n_clients;
     }
ffea644c
 #endif
 }
 
6fbf66fa
 static bool
81d882d5
 learn_address_script(const struct multi_context *m,
                      const struct multi_instance *mi,
                      const char *op,
                      const struct mroute_addr *addr)
 {
     struct gc_arena gc = gc_new();
     struct env_set *es;
     bool ret = true;
     struct plugin_list *plugins;
 
     /* get environmental variable source */
     if (mi && mi->context.c2.es)
     {
         es = mi->context.c2.es;
     }
     else
     {
         es = env_set_create(&gc);
     }
 
     /* get plugin source */
     if (mi)
     {
         plugins = mi->context.plugins;
     }
     else
     {
         plugins = m->top.plugins;
     }
 
     if (plugin_defined(plugins, OPENVPN_PLUGIN_LEARN_ADDRESS))
     {
         struct argv argv = argv_new();
         argv_printf(&argv, "%s %s",
                     op,
                     mroute_addr_print(addr, &gc));
         if (mi)
         {
             argv_printf_cat(&argv, "%s", tls_common_name(mi->context.c2.tls_multi, false));
         }
         if (plugin_call(plugins, OPENVPN_PLUGIN_LEARN_ADDRESS, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
         {
             msg(M_WARN, "WARNING: learn-address plugin call failed");
             ret = false;
         }
         argv_reset(&argv);
     }
 
     if (m->top.options.learn_address_script)
     {
         struct argv argv = argv_new();
         setenv_str(es, "script_type", "learn-address");
         argv_parse_cmd(&argv, m->top.options.learn_address_script);
         argv_printf_cat(&argv, "%s %s", op, mroute_addr_print(addr, &gc));
         if (mi)
         {
             argv_printf_cat(&argv, "%s", tls_common_name(mi->context.c2.tls_multi, false));
         }
         if (!openvpn_run_script(&argv, es, 0, "--learn-address"))
         {
             ret = false;
         }
         argv_reset(&argv);
     }
 
     gc_free(&gc);
     return ret;
6fbf66fa
 }
 
 void
81d882d5
 multi_ifconfig_pool_persist(struct multi_context *m, bool force)
6fbf66fa
 {
81d882d5
     /* write pool data to file */
     if (m->ifconfig_pool
         && m->top.c1.ifconfig_pool_persist
         && (force || ifconfig_pool_write_trigger(m->top.c1.ifconfig_pool_persist)))
6fbf66fa
     {
81d882d5
         ifconfig_pool_write(m->top.c1.ifconfig_pool_persist, m->ifconfig_pool);
6fbf66fa
     }
 }
 
 static void
81d882d5
 multi_reap_range(const struct multi_context *m,
                  int start_bucket,
                  int end_bucket)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     struct hash_iterator hi;
     struct hash_element *he;
6fbf66fa
 
81d882d5
     if (start_bucket < 0)
6fbf66fa
     {
81d882d5
         start_bucket = 0;
         end_bucket = hash_n_buckets(m->vhash);
6fbf66fa
     }
 
81d882d5
     dmsg(D_MULTI_DEBUG, "MULTI: REAP range %d -> %d", start_bucket, end_bucket);
     hash_iterator_init_range(m->vhash, &hi, start_bucket, end_bucket);
     while ((he = hash_iterator_next(&hi)) != NULL)
6fbf66fa
     {
81d882d5
         struct multi_route *r = (struct multi_route *) he->value;
         if (!multi_route_defined(m, r))
         {
             dmsg(D_MULTI_DEBUG, "MULTI: REAP DEL %s",
                  mroute_addr_print(&r->addr, &gc));
             learn_address_script(m, NULL, "delete", &r->addr);
             multi_route_del(r);
             hash_iterator_delete_element(&hi);
         }
6fbf66fa
     }
81d882d5
     hash_iterator_free(&hi);
     gc_free(&gc);
6fbf66fa
 }
 
 static void
81d882d5
 multi_reap_all(const struct multi_context *m)
6fbf66fa
 {
81d882d5
     multi_reap_range(m, -1, 0);
6fbf66fa
 }
 
 static struct multi_reap *
81d882d5
 multi_reap_new(int buckets_per_pass)
6fbf66fa
 {
81d882d5
     struct multi_reap *mr;
     ALLOC_OBJ(mr, struct multi_reap);
     mr->bucket_base = 0;
     mr->buckets_per_pass = buckets_per_pass;
     mr->last_call = now;
     return mr;
6fbf66fa
 }
 
 void
81d882d5
 multi_reap_process_dowork(const struct multi_context *m)
6fbf66fa
 {
81d882d5
     struct multi_reap *mr = m->reaper;
     if (mr->bucket_base >= hash_n_buckets(m->vhash))
     {
         mr->bucket_base = 0;
     }
     multi_reap_range(m, mr->bucket_base, mr->bucket_base + mr->buckets_per_pass);
     mr->bucket_base += mr->buckets_per_pass;
     mr->last_call = now;
6fbf66fa
 }
 
 static void
81d882d5
 multi_reap_free(struct multi_reap *mr)
6fbf66fa
 {
81d882d5
     free(mr);
6fbf66fa
 }
 
 /*
  * How many buckets in vhash to reap per pass.
  */
 static int
81d882d5
 reap_buckets_per_pass(int n_buckets)
6fbf66fa
 {
81d882d5
     return constrain_int(n_buckets / REAP_DIVISOR, REAP_MIN, REAP_MAX);
6fbf66fa
 }
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
 
 static uint32_t
81d882d5
 cid_hash_function(const void *key, uint32_t iv)
90efcacb
 {
81d882d5
     const unsigned long *k = (const unsigned long *)key;
     return (uint32_t) *k;
90efcacb
 }
 
 static bool
81d882d5
 cid_compare_function(const void *key1, const void *key2)
90efcacb
 {
81d882d5
     const unsigned long *k1 = (const unsigned long *)key1;
     const unsigned long *k2 = (const unsigned long *)key2;
     return *k1 == *k2;
90efcacb
 }
 
 #endif
 
0d1a75bf
 #ifdef ENABLE_ASYNC_PUSH
 static uint32_t
 /*
  * inotify watcher descriptors are used as hash value
  */
81d882d5
 int_hash_function(const void *key, uint32_t iv)
0d1a75bf
 {
81d882d5
     return (unsigned long)key;
0d1a75bf
 }
 
 static bool
81d882d5
 int_compare_function(const void *key1, const void *key2)
0d1a75bf
 {
81d882d5
     return (unsigned long)key1 == (unsigned long)key2;
0d1a75bf
 }
 #endif
 
6fbf66fa
 /*
  * Main initialization function, init multi_context object.
  */
 void
81d882d5
 multi_init(struct multi_context *m, struct context *t, bool tcp_mode, int thread_mode)
 {
     int dev = DEV_TYPE_UNDEF;
 
     msg(D_MULTI_LOW, "MULTI: multi_init called, r=%d v=%d",
         t->options.real_hash_size,
         t->options.virtual_hash_size);
 
     /*
      * Get tun/tap/null device type
      */
     dev = dev_type_enum(t->options.dev, t->options.dev_type);
 
     /*
      * Init our multi_context object.
      */
     CLEAR(*m);
 
     m->thread_mode = thread_mode;
 
     /*
      * Real address hash table (source port number is
      * considered to be part of the address).  Used
      * to determine which client sent an incoming packet
      * which is seen on the TCP/UDP socket.
      */
     m->hash = hash_init(t->options.real_hash_size,
                         get_random(),
                         mroute_addr_hash_function,
                         mroute_addr_compare_function);
 
     /*
      * Virtual address hash table.  Used to determine
      * which client to route a packet to.
      */
     m->vhash = hash_init(t->options.virtual_hash_size,
                          get_random(),
                          mroute_addr_hash_function,
                          mroute_addr_compare_function);
 
     /*
      * This hash table is a clone of m->hash but with a
      * bucket size of one so that it can be used
      * for fast iteration through the list.
      */
     m->iter = hash_init(1,
                         get_random(),
                         mroute_addr_hash_function,
                         mroute_addr_compare_function);
6fbf66fa
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
     m->cid_hash = hash_init(t->options.real_hash_size,
                             0,
                             cid_hash_function,
                             cid_compare_function);
90efcacb
 #endif
 
0d1a75bf
 #ifdef ENABLE_ASYNC_PUSH
81d882d5
     /*
      * Mapping between inotify watch descriptors and
      * multi_instances.
      */
     m->inotify_watchers = hash_init(t->options.real_hash_size,
                                     get_random(),
                                     int_hash_function,
                                     int_compare_function);
0d1a75bf
 #endif
 
81d882d5
     /*
      * This is our scheduler, for time-based wakeup
      * events.
      */
     m->schedule = schedule_init();
 
     /*
      * Limit frequency of incoming connections to control
      * DoS.
      */
     m->new_connection_limiter = frequency_limit_init(t->options.cf_max,
                                                      t->options.cf_per);
 
     /*
      * Allocate broadcast/multicast buffer list
      */
     m->mbuf = mbuf_init(t->options.n_bcast_buf);
 
     /*
      * Different status file format options are available
      */
     m->status_file_version = t->options.status_file_version;
 
     /*
      * Possibly allocate an ifconfig pool, do it
      * differently based on whether a tun or tap style
      * tunnel.
      */
     if (t->options.ifconfig_pool_defined)
     {
         int pool_type = IFCONFIG_POOL_INDIV;
 
         if (dev == DEV_TYPE_TUN && t->options.topology == TOP_NET30)
         {
             pool_type = IFCONFIG_POOL_30NET;
         }
 
         m->ifconfig_pool = ifconfig_pool_init(pool_type,
                                               t->options.ifconfig_pool_start,
                                               t->options.ifconfig_pool_end,
                                               t->options.duplicate_cn,
                                               t->options.ifconfig_ipv6_pool_defined,
                                               t->options.ifconfig_ipv6_pool_base,
                                               t->options.ifconfig_ipv6_pool_netbits );
 
         /* reload pool data from file */
         if (t->c1.ifconfig_pool_persist)
         {
             ifconfig_pool_read(t->c1.ifconfig_pool_persist, m->ifconfig_pool);
         }
     }
 
     /*
      * Help us keep track of routing table.
      */
     m->route_helper = mroute_helper_init(MULTI_CACHE_ROUTE_TTL);
 
     /*
      * Initialize route and instance reaper.
      */
     m->reaper = multi_reap_new(reap_buckets_per_pass(t->options.virtual_hash_size));
 
     /*
      * Get local ifconfig address
      */
     CLEAR(m->local);
     ASSERT(t->c1.tuntap);
     mroute_extract_in_addr_t(&m->local, t->c1.tuntap->local);
 
     /*
      * Per-client limits
      */
     m->max_clients = t->options.max_clients;
 
     m->instances = calloc(m->max_clients, sizeof(struct multi_instance *));
 
     /*
      * Initialize multi-socket TCP I/O wait object
      */
     if (tcp_mode)
     {
         m->mtcp = multi_tcp_init(t->options.max_clients, &m->max_clients);
     }
     m->tcp_queue_limit = t->options.tcp_queue_limit;
 
     /*
      * Allow client <-> client communication, without going through
      * tun/tap interface and network stack?
      */
     m->enable_c2c = t->options.enable_c2c;
 
     /* initialize stale routes check timer */
     if (t->options.stale_routes_check_interval > 0)
     {
         msg(M_INFO, "Initializing stale route check timer to run every %i seconds and to removing routes with activity timeout older than %i seconds",
             t->options.stale_routes_check_interval, t->options.stale_routes_ageing_time);
         event_timeout_init(&m->stale_routes_check_et, t->options.stale_routes_check_interval, 0);
     }
 
     m->deferred_shutdown_signal.signal_received = 0;
6fbf66fa
 }
 
 const char *
81d882d5
 multi_instance_string(const struct multi_instance *mi, bool null, struct gc_arena *gc)
6fbf66fa
 {
81d882d5
     if (mi)
6fbf66fa
     {
81d882d5
         struct buffer out = alloc_buf_gc(MULTI_PREFIX_MAX_LENGTH, gc);
         const char *cn = tls_common_name(mi->context.c2.tls_multi, true);
6fbf66fa
 
81d882d5
         if (cn)
         {
             buf_printf(&out, "%s/", cn);
         }
         buf_printf(&out, "%s", mroute_addr_print(&mi->real, gc));
         return BSTR(&out);
     }
     else if (null)
     {
         return NULL;
     }
     else
     {
         return "UNDEF";
6fbf66fa
     }
 }
 
72bcdfdc
 static void
81d882d5
 generate_prefix(struct multi_instance *mi)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     const char *prefix = multi_instance_string(mi, true, &gc);
     if (prefix)
     {
         strncpynt(mi->msg_prefix, prefix, sizeof(mi->msg_prefix));
     }
     else
     {
         mi->msg_prefix[0] = '\0';
     }
     set_prefix(mi);
     gc_free(&gc);
6fbf66fa
 }
 
 void
81d882d5
 ungenerate_prefix(struct multi_instance *mi)
6fbf66fa
 {
81d882d5
     mi->msg_prefix[0] = '\0';
     set_prefix(mi);
6fbf66fa
 }
 
90efcacb
 static const char *
81d882d5
 mi_prefix(const struct multi_instance *mi)
90efcacb
 {
81d882d5
     if (mi && mi->msg_prefix[0])
     {
         return mi->msg_prefix;
     }
     else
     {
         return "UNDEF_I";
     }
90efcacb
 }
 
6fbf66fa
 /*
  * Tell the route helper about deleted iroutes so
  * that it can update its mask of currently used
  * CIDR netlengths.
  */
 static void
81d882d5
 multi_del_iroutes(struct multi_context *m,
                   struct multi_instance *mi)
6fbf66fa
 {
81d882d5
     const struct iroute *ir;
     const struct iroute_ipv6 *ir6;
     if (TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN)
6fbf66fa
     {
81d882d5
         for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
4cd4899e
         {
81d882d5
             mroute_helper_del_iroute46(m->route_helper, ir->netbits);
4cd4899e
         }
512cda46
 
81d882d5
         for (ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next)
4cd4899e
         {
81d882d5
             mroute_helper_del_iroute46(m->route_helper, ir6->netbits);
4cd4899e
         }
6fbf66fa
     }
 }
 
 static void
81d882d5
 setenv_stats(struct context *c)
5f435d64
 {
81d882d5
     setenv_counter(c->c2.es, "bytes_received", c->c2.link_read_bytes);
     setenv_counter(c->c2.es, "bytes_sent", c->c2.link_write_bytes);
5f435d64
 }
 
 static void
81d882d5
 multi_client_disconnect_setenv(struct multi_context *m,
                                struct multi_instance *mi)
6fbf66fa
 {
81d882d5
     /* setenv client real IP address */
     setenv_trusted(mi->context.c2.es, get_link_socket_info(&mi->context));
6fbf66fa
 
81d882d5
     /* setenv stats */
     setenv_stats(&mi->context);
6fbf66fa
 
81d882d5
     /* setenv connection duration */
6c69693a
     setenv_long_long(mi->context.c2.es, "time_duration", now - mi->created);
6fbf66fa
 }
 
 static void
81d882d5
 multi_client_disconnect_script(struct multi_context *m,
                                struct multi_instance *mi)
 {
     if ((mi->context.c2.context_auth == CAS_SUCCEEDED && mi->connection_established_flag)
         || mi->context.c2.context_auth == CAS_PARTIAL)
     {
         multi_client_disconnect_setenv(m, mi);
 
         if (plugin_defined(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT))
         {
             if (plugin_call(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT, NULL, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
             {
                 msg(M_WARN, "WARNING: client-disconnect plugin call failed");
             }
         }
 
         if (mi->context.options.client_disconnect_script)
         {
             struct argv argv = argv_new();
             setenv_str(mi->context.c2.es, "script_type", "client-disconnect");
             argv_parse_cmd(&argv, mi->context.options.client_disconnect_script);
             openvpn_run_script(&argv, mi->context.c2.es, 0, "--client-disconnect");
             argv_reset(&argv);
         }
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
         if (management)
         {
             management_notify_client_close(management, &mi->context.c2.mda_context, mi->context.c2.es);
         }
90efcacb
 #endif
 
6fbf66fa
     }
 }
 
 void
81d882d5
 multi_close_instance(struct multi_context *m,
                      struct multi_instance *mi,
                      bool shutdown)
 {
     perf_push(PERF_MULTI_CLOSE_INSTANCE);
 
     ASSERT(!mi->halt);
     mi->halt = true;
 
     dmsg(D_MULTI_DEBUG, "MULTI: multi_close_instance called");
 
     /* adjust current client connection count */
     m->n_clients += mi->n_clients_delta;
     update_mstat_n_clients(m->n_clients);
     mi->n_clients_delta = 0;
 
     /* prevent dangling pointers */
     if (m->pending == mi)
     {
         multi_set_pending(m, NULL);
     }
     if (m->earliest_wakeup == mi)
     {
         m->earliest_wakeup = NULL;
     }
 
     if (!shutdown)
     {
         if (mi->did_real_hash)
         {
             ASSERT(hash_remove(m->hash, &mi->real));
         }
         if (mi->did_iter)
         {
             ASSERT(hash_remove(m->iter, &mi->real));
         }
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
         if (mi->did_cid_hash)
         {
             ASSERT(hash_remove(m->cid_hash, &mi->context.c2.mda_context.cid));
         }
90efcacb
 #endif
6fbf66fa
 
0d1a75bf
 #ifdef ENABLE_ASYNC_PUSH
81d882d5
         if (mi->inotify_watch != -1)
         {
             hash_remove(m->inotify_watchers, (void *) (unsigned long)mi->inotify_watch);
             mi->inotify_watch = -1;
         }
0d1a75bf
 #endif
 
81d882d5
         if (mi->context.c2.tls_multi->peer_id != MAX_PEER_ID)
         {
             m->instances[mi->context.c2.tls_multi->peer_id] = NULL;
         }
65eedc35
 
81d882d5
         schedule_remove_entry(m->schedule, (struct schedule_entry *) mi);
6fbf66fa
 
81d882d5
         ifconfig_pool_release(m->ifconfig_pool, mi->vaddr_handle, false);
 
         if (mi->did_iroutes)
6fbf66fa
         {
81d882d5
             multi_del_iroutes(m, mi);
             mi->did_iroutes = false;
6fbf66fa
         }
 
81d882d5
         if (m->mtcp)
         {
             multi_tcp_dereference_instance(m->mtcp, mi);
         }
6fbf66fa
 
81d882d5
         mbuf_dereference_instance(m->mbuf, mi);
6fbf66fa
     }
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
     set_cc_config(mi, NULL);
90efcacb
 #endif
 
81d882d5
     multi_client_disconnect_script(m, mi);
6fbf66fa
 
81d882d5
     if (mi->did_open_context)
     {
         close_context(&mi->context, SIGTERM, CC_GC_FREE);
     }
6fbf66fa
 
81d882d5
     multi_tcp_instance_specific_free(mi);
6fbf66fa
 
81d882d5
     ungenerate_prefix(mi);
6fbf66fa
 
81d882d5
     /*
      * Don't actually delete the instance memory allocation yet,
      * because virtual routes may still point to it.  Let the
      * vhash reaper deal with it.
      */
     multi_instance_dec_refcount(mi);
6fbf66fa
 
81d882d5
     perf_pop();
6fbf66fa
 }
 
 /*
  * Called on shutdown or restart.
  */
 void
81d882d5
 multi_uninit(struct multi_context *m)
6fbf66fa
 {
81d882d5
     if (m->thread_mode & MC_WORK_THREAD)
6fbf66fa
     {
81d882d5
         multi_top_free(m);
         m->thread_mode = MC_UNDEF;
6fbf66fa
     }
81d882d5
     else if (m->thread_mode)
6fbf66fa
     {
81d882d5
         if (m->hash)
         {
             struct hash_iterator hi;
             struct hash_element *he;
 
             hash_iterator_init(m->iter, &hi);
             while ((he = hash_iterator_next(&hi)))
             {
                 struct multi_instance *mi = (struct multi_instance *) he->value;
                 mi->did_iter = false;
                 multi_close_instance(m, mi, true);
             }
             hash_iterator_free(&hi);
 
             multi_reap_all(m);
 
             hash_free(m->hash);
             hash_free(m->vhash);
             hash_free(m->iter);
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
             hash_free(m->cid_hash);
90efcacb
 #endif
81d882d5
             m->hash = NULL;
6fbf66fa
 
81d882d5
             free(m->instances);
65eedc35
 
0d1a75bf
 #ifdef ENABLE_ASYNC_PUSH
81d882d5
             hash_free(m->inotify_watchers);
             m->inotify_watchers = NULL;
0d1a75bf
 #endif
 
81d882d5
             schedule_free(m->schedule);
             mbuf_free(m->mbuf);
             ifconfig_pool_free(m->ifconfig_pool);
             frequency_limit_free(m->new_connection_limiter);
             multi_reap_free(m->reaper);
             mroute_helper_free(m->route_helper);
             multi_tcp_free(m->mtcp);
             m->thread_mode = MC_UNDEF;
         }
6fbf66fa
     }
 }
 
 /*
  * Create a client instance object for a newly connected client.
  */
 struct multi_instance *
81d882d5
 multi_create_instance(struct multi_context *m, const struct mroute_addr *real)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     struct multi_instance *mi;
6fbf66fa
 
81d882d5
     perf_push(PERF_MULTI_CREATE_INSTANCE);
6fbf66fa
 
81d882d5
     msg(D_MULTI_MEDIUM, "MULTI: multi_create_instance called");
6fbf66fa
 
81d882d5
     ALLOC_OBJ_CLEAR(mi, struct multi_instance);
6fbf66fa
 
81d882d5
     mi->gc = gc_new();
     multi_instance_inc_refcount(mi);
     mi->vaddr_handle = -1;
     mi->created = now;
     mroute_addr_init(&mi->real);
6fbf66fa
 
81d882d5
     if (real)
6fbf66fa
     {
81d882d5
         mi->real = *real;
         generate_prefix(mi);
6fbf66fa
     }
 
81d882d5
     mi->did_open_context = true;
     inherit_context_child(&mi->context, &m->top);
     if (IS_SIG(&mi->context))
     {
         goto err;
     }
6fbf66fa
 
81d882d5
     mi->context.c2.context_auth = CAS_PENDING;
6fbf66fa
 
81d882d5
     if (hash_n_elements(m->hash) >= m->max_clients)
6fbf66fa
     {
81d882d5
         msg(D_MULTI_ERRORS, "MULTI: new incoming connection would exceed maximum number of clients (%d)", m->max_clients);
         goto err;
6fbf66fa
     }
 
81d882d5
     if (!real) /* TCP mode? */
6fbf66fa
     {
81d882d5
         if (!multi_tcp_instance_specific_init(m, mi))
         {
             goto err;
         }
         generate_prefix(mi);
6fbf66fa
     }
 
81d882d5
     if (!hash_add(m->iter, &mi->real, mi, false))
6fbf66fa
     {
81d882d5
         msg(D_MULTI_LOW, "MULTI: unable to add real address [%s] to iterator hash table",
             mroute_addr_print(&mi->real, &gc));
         goto err;
6fbf66fa
     }
81d882d5
     mi->did_iter = true;
6fbf66fa
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
4cd4899e
     do
     {
81d882d5
         mi->context.c2.mda_context.cid = m->cid_counter++;
     } while (!hash_add(m->cid_hash, &mi->context.c2.mda_context.cid, mi, false));
     mi->did_cid_hash = true;
90efcacb
 #endif
 
81d882d5
     mi->context.c2.push_reply_deferred = true;
6fbf66fa
 
0d1a75bf
 #ifdef ENABLE_ASYNC_PUSH
81d882d5
     mi->context.c2.push_request_received = false;
     mi->inotify_watch = -1;
0d1a75bf
 #endif
 
81d882d5
     if (!multi_process_post(m, mi, MPP_PRE_SELECT))
6fbf66fa
     {
81d882d5
         msg(D_MULTI_ERRORS, "MULTI: signal occurred during client instance initialization");
         goto err;
6fbf66fa
     }
 
81d882d5
     perf_pop();
     gc_free(&gc);
     return mi;
6fbf66fa
 
81d882d5
 err:
     multi_close_instance(m, mi, false);
     perf_pop();
     gc_free(&gc);
     return NULL;
6fbf66fa
 }
 
 /*
  * Dump tables -- triggered by SIGUSR2.
  * If status file is defined, write to file.
  * If status file is NULL, write to syslog.
  */
 void
81d882d5
 multi_print_status(struct multi_context *m, struct status_output *so, const int version)
 {
     if (m->hash)
     {
         struct gc_arena gc_top = gc_new();
         struct hash_iterator hi;
         const struct hash_element *he;
 
         status_reset(so);
 
         if (version == 1) /* WAS: m->status_file_version */
         {
             /*
              * Status file version 1
              */
             status_printf(so, "OpenVPN CLIENT LIST");
             status_printf(so, "Updated,%s", time_string(0, 0, false, &gc_top));
             status_printf(so, "Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since");
             hash_iterator_init(m->hash, &hi);
             while ((he = hash_iterator_next(&hi)))
             {
                 struct gc_arena gc = gc_new();
                 const struct multi_instance *mi = (struct multi_instance *) he->value;
 
                 if (!mi->halt)
                 {
                     status_printf(so, "%s,%s," counter_format "," counter_format ",%s",
                                   tls_common_name(mi->context.c2.tls_multi, false),
                                   mroute_addr_print(&mi->real, &gc),
                                   mi->context.c2.link_read_bytes,
                                   mi->context.c2.link_write_bytes,
                                   time_string(mi->created, 0, false, &gc));
                 }
                 gc_free(&gc);
             }
             hash_iterator_free(&hi);
 
             status_printf(so, "ROUTING TABLE");
             status_printf(so, "Virtual Address,Common Name,Real Address,Last Ref");
             hash_iterator_init(m->vhash, &hi);
             while ((he = hash_iterator_next(&hi)))
             {
                 struct gc_arena gc = gc_new();
                 const struct multi_route *route = (struct multi_route *) he->value;
 
                 if (multi_route_defined(m, route))
                 {
                     const struct multi_instance *mi = route->instance;
                     const struct mroute_addr *ma = &route->addr;
                     char flags[2] = {0, 0};
 
                     if (route->flags & MULTI_ROUTE_CACHE)
                     {
                         flags[0] = 'C';
                     }
                     status_printf(so, "%s%s,%s,%s,%s",
                                   mroute_addr_print(ma, &gc),
                                   flags,
                                   tls_common_name(mi->context.c2.tls_multi, false),
                                   mroute_addr_print(&mi->real, &gc),
                                   time_string(route->last_reference, 0, false, &gc));
                 }
                 gc_free(&gc);
             }
             hash_iterator_free(&hi);
 
             status_printf(so, "GLOBAL STATS");
             if (m->mbuf)
             {
                 status_printf(so, "Max bcast/mcast queue length,%d",
                               mbuf_maximum_queued(m->mbuf));
             }
 
             status_printf(so, "END");
         }
         else if (version == 2 || version == 3)
         {
             const char sep = (version == 3) ? '\t' : ',';
 
             /*
              * Status file version 2 and 3
              */
             status_printf(so, "TITLE%c%s", sep, title_string);
             status_printf(so, "TIME%c%s%c%u", sep, time_string(now, 0, false, &gc_top), sep, (unsigned int)now);
8acc40b6
             status_printf(so, "HEADER%cCLIENT_LIST%cCommon Name%cReal Address%cVirtual Address%cVirtual IPv6 Address%cBytes Received%cBytes Sent%cConnected Since%cConnected Since (time_t)%cUsername%cClient ID%cPeer ID%cData Channel Cipher",
                           sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep);
81d882d5
             hash_iterator_init(m->hash, &hi);
             while ((he = hash_iterator_next(&hi)))
             {
                 struct gc_arena gc = gc_new();
                 const struct multi_instance *mi = (struct multi_instance *) he->value;
 
                 if (!mi->halt)
                 {
                     status_printf(so, "CLIENT_LIST%c%s%c%s%c%s%c%s%c" counter_format "%c" counter_format "%c%s%c%u%c%s%c"
01cfa61a
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
                                   "%lu"
01cfa61a
 #else
81d882d5
                                   ""
01cfa61a
 #endif
8acc40b6
                                   "%c%" PRIu32 "%c%s",
81d882d5
                                   sep, tls_common_name(mi->context.c2.tls_multi, false),
                                   sep, mroute_addr_print(&mi->real, &gc),
                                   sep, print_in_addr_t(mi->reporting_addr, IA_EMPTY_IF_UNDEF, &gc),
                                   sep, print_in6_addr(mi->reporting_addr_ipv6, IA_EMPTY_IF_UNDEF, &gc),
                                   sep, mi->context.c2.link_read_bytes,
                                   sep, mi->context.c2.link_write_bytes,
                                   sep, time_string(mi->created, 0, false, &gc),
                                   sep, (unsigned int)mi->created,
                                   sep, tls_username(mi->context.c2.tls_multi, false),
01cfa61a
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
                                   sep, mi->context.c2.mda_context.cid,
01cfa61a
 #else
81d882d5
                                   sep,
01cfa61a
 #endif
8acc40b6
                                   sep, mi->context.c2.tls_multi ? mi->context.c2.tls_multi->peer_id : UINT32_MAX,
                                   sep, translate_cipher_name_to_openvpn(mi->context.options.ciphername));
81d882d5
                 }
                 gc_free(&gc);
             }
             hash_iterator_free(&hi);
 
             status_printf(so, "HEADER%cROUTING_TABLE%cVirtual Address%cCommon Name%cReal Address%cLast Ref%cLast Ref (time_t)",
                           sep, sep, sep, sep, sep, sep);
             hash_iterator_init(m->vhash, &hi);
             while ((he = hash_iterator_next(&hi)))
             {
                 struct gc_arena gc = gc_new();
                 const struct multi_route *route = (struct multi_route *) he->value;
 
                 if (multi_route_defined(m, route))
                 {
                     const struct multi_instance *mi = route->instance;
                     const struct mroute_addr *ma = &route->addr;
                     char flags[2] = {0, 0};
 
                     if (route->flags & MULTI_ROUTE_CACHE)
                     {
                         flags[0] = 'C';
                     }
                     status_printf(so, "ROUTING_TABLE%c%s%s%c%s%c%s%c%s%c%u",
                                   sep, mroute_addr_print(ma, &gc), flags,
                                   sep, tls_common_name(mi->context.c2.tls_multi, false),
                                   sep, mroute_addr_print(&mi->real, &gc),
                                   sep, time_string(route->last_reference, 0, false, &gc),
                                   sep, (unsigned int)route->last_reference);
                 }
                 gc_free(&gc);
             }
             hash_iterator_free(&hi);
 
             if (m->mbuf)
             {
                 status_printf(so, "GLOBAL_STATS%cMax bcast/mcast queue length%c%d",
                               sep, sep, mbuf_maximum_queued(m->mbuf));
             }
 
             status_printf(so, "END");
         }
         else
         {
             status_printf(so, "ERROR: bad status format version number");
         }
3c7f2f55
 
 #ifdef PACKET_TRUNCATION_CHECK
81d882d5
         {
             status_printf(so, "HEADER,ERRORS,Common Name,TUN Read Trunc,TUN Write Trunc,Pre-encrypt Trunc,Post-decrypt Trunc");
             hash_iterator_init(m->hash, &hi);
             while ((he = hash_iterator_next(&hi)))
             {
                 struct gc_arena gc = gc_new();
                 const struct multi_instance *mi = (struct multi_instance *) he->value;
 
                 if (!mi->halt)
                 {
                     status_printf(so, "ERRORS,%s," counter_format "," counter_format "," counter_format "," counter_format,
                                   tls_common_name(mi->context.c2.tls_multi, false),
                                   m->top.c2.n_trunc_tun_read,
                                   mi->context.c2.n_trunc_tun_write,
                                   mi->context.c2.n_trunc_pre_encrypt,
                                   mi->context.c2.n_trunc_post_decrypt);
                 }
                 gc_free(&gc);
             }
             hash_iterator_free(&hi);
         }
 #endif /* ifdef PACKET_TRUNCATION_CHECK */
3c7f2f55
 
81d882d5
         status_flush(so);
         gc_free(&gc_top);
6fbf66fa
     }
0d1a75bf
 
 #ifdef ENABLE_ASYNC_PUSH
81d882d5
     if (m->inotify_watchers)
     {
         msg(D_MULTI_DEBUG, "inotify watchers count: %d\n", hash_n_elements(m->inotify_watchers));
     }
0d1a75bf
 #endif
6fbf66fa
 }
 
 /*
  * Learn a virtual address or route.
  * The learn will fail if the learn address
  * script/plugin fails.  In this case the
  * return value may be != mi.
  * Return the instance which owns this route,
  * or NULL if none.
  */
 static struct multi_instance *
81d882d5
 multi_learn_addr(struct multi_context *m,
                  struct multi_instance *mi,
                  const struct mroute_addr *addr,
                  const unsigned int flags)
 {
     struct hash_element *he;
     const uint32_t hv = hash_value(m->vhash, addr);
     struct hash_bucket *bucket = hash_bucket(m->vhash, hv);
     struct multi_route *oldroute = NULL;
     struct multi_instance *owner = NULL;
a19c56db
     struct gc_arena gc = gc_new();
81d882d5
 
     /* if route currently exists, get the instance which owns it */
     he = hash_lookup_fast(m->vhash, bucket, addr, hv);
     if (he)
     {
         oldroute = (struct multi_route *) he->value;
     }
     if (oldroute && multi_route_defined(m, oldroute))
     {
         owner = oldroute->instance;
     }
 
     /* do we need to add address to hash table? */
a19c56db
     if ((!owner || owner != mi) && mroute_learnable_address(addr, &gc)
81d882d5
         && !mroute_addr_equal(addr, &m->local))
     {
         struct multi_route *newroute;
         bool learn_succeeded = false;
 
         ALLOC_OBJ(newroute, struct multi_route);
         newroute->addr = *addr;
         newroute->instance = mi;
         newroute->flags = flags;
         newroute->last_reference = now;
         newroute->cache_generation = 0;
 
         /* The cache is invalidated when cache_generation is incremented */
         if (flags & MULTI_ROUTE_CACHE)
         {
             newroute->cache_generation = m->route_helper->cache_generation;
         }
 
         if (oldroute) /* route already exists? */
         {
             if (route_quota_test(m, mi) && learn_address_script(m, mi, "update", &newroute->addr))
             {
                 learn_succeeded = true;
                 owner = mi;
                 multi_instance_inc_refcount(mi);
                 route_quota_inc(mi);
 
                 /* delete old route */
                 multi_route_del(oldroute);
 
                 /* modify hash table entry, replacing old route */
                 he->key = &newroute->addr;
                 he->value = newroute;
             }
         }
         else
         {
             if (route_quota_test(m, mi) && learn_address_script(m, mi, "add", &newroute->addr))
             {
                 learn_succeeded = true;
                 owner = mi;
                 multi_instance_inc_refcount(mi);
                 route_quota_inc(mi);
 
                 /* add new route */
                 hash_add_fast(m->vhash, bucket, &newroute->addr, hv, newroute);
             }
         }
 
         msg(D_MULTI_LOW, "MULTI: Learn%s: %s -> %s",
             learn_succeeded ? "" : " FAILED",
             mroute_addr_print(&newroute->addr, &gc),
             multi_instance_string(mi, false, &gc));
 
         if (!learn_succeeded)
         {
             free(newroute);
         }
     }
a19c56db
     gc_free(&gc);
81d882d5
 
     return owner;
6fbf66fa
 }
 
 /*
  * Get client instance based on virtual address.
  */
 static struct multi_instance *
81d882d5
 multi_get_instance_by_virtual_addr(struct multi_context *m,
                                    const struct mroute_addr *addr,
                                    bool cidr_routing)
6fbf66fa
 {
81d882d5
     struct multi_route *route;
     struct multi_instance *ret = NULL;
6fbf66fa
 
81d882d5
     /* check for local address */
     if (mroute_addr_equal(addr, &m->local))
     {
         return NULL;
     }
6fbf66fa
 
81d882d5
     route = (struct multi_route *) hash_lookup(m->vhash, addr);
6fbf66fa
 
81d882d5
     /* does host route (possible cached) exist? */
     if (route && multi_route_defined(m, route))
6fbf66fa
     {
81d882d5
         struct multi_instance *mi = route->instance;
         route->last_reference = now;
         ret = mi;
6fbf66fa
     }
81d882d5
     else if (cidr_routing) /* do we need to regenerate a host route cache entry? */
6fbf66fa
     {
81d882d5
         struct mroute_helper *rh = m->route_helper;
         struct mroute_addr tryaddr;
         int i;
6fbf66fa
 
81d882d5
         /* cycle through each CIDR length */
         for (i = 0; i < rh->n_net_len; ++i)
         {
             tryaddr = *addr;
             tryaddr.type |= MR_WITH_NETBITS;
             tryaddr.netbits = rh->net_len[i];
             mroute_addr_mask_host_bits(&tryaddr);
 
             /* look up a possible route with netbits netmask */
             route = (struct multi_route *) hash_lookup(m->vhash, &tryaddr);
 
             if (route && multi_route_defined(m, route))
             {
                 /* found an applicable route, cache host route */
                 struct multi_instance *mi = route->instance;
                 multi_learn_addr(m, mi, addr, MULTI_ROUTE_CACHE|MULTI_ROUTE_AGEABLE);
                 ret = mi;
                 break;
             }
         }
6fbf66fa
     }
81d882d5
 
6fbf66fa
 #ifdef ENABLE_DEBUG
81d882d5
     if (check_debug_level(D_MULTI_DEBUG))
     {
         struct gc_arena gc = gc_new();
         const char *addr_text = mroute_addr_print(addr, &gc);
         if (ret)
         {
             dmsg(D_MULTI_DEBUG, "GET INST BY VIRT: %s -> %s via %s",
                  addr_text,
                  multi_instance_string(ret, false, &gc),
                  mroute_addr_print(&route->addr, &gc));
         }
         else
         {
             dmsg(D_MULTI_DEBUG, "GET INST BY VIRT: %s [failed]",
                  addr_text);
         }
         gc_free(&gc);
6fbf66fa
     }
 #endif
 
81d882d5
     ASSERT(!(ret && ret->halt));
     return ret;
6fbf66fa
 }
 
 /*
  * Helper function to multi_learn_addr().
  */
 static struct multi_instance *
81d882d5
 multi_learn_in_addr_t(struct multi_context *m,
                       struct multi_instance *mi,
                       in_addr_t a,
                       int netbits,  /* -1 if host route, otherwise # of network bits in address */
                       bool primary)
6fbf66fa
 {
81d882d5
     struct openvpn_sockaddr remote_si;
     struct mroute_addr addr;
6fbf66fa
 
81d882d5
     CLEAR(remote_si);
     remote_si.addr.in4.sin_family = AF_INET;
     remote_si.addr.in4.sin_addr.s_addr = htonl(a);
     ASSERT(mroute_extract_openvpn_sockaddr(&addr, &remote_si, false));
6fbf66fa
 
81d882d5
     if (netbits >= 0)
6fbf66fa
     {
81d882d5
         addr.type |= MR_WITH_NETBITS;
         addr.netbits = (uint8_t) netbits;
6fbf66fa
     }
90efcacb
 
81d882d5
     {
         struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
         if (management && owner)
         {
             management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
         }
90efcacb
 #endif
81d882d5
         return owner;
     }
6fbf66fa
 }
 
512cda46
 static struct multi_instance *
81d882d5
 multi_learn_in6_addr(struct multi_context *m,
                      struct multi_instance *mi,
                      struct in6_addr a6,
                      int netbits,   /* -1 if host route, otherwise # of network bits in address */
                      bool primary)
512cda46
 {
81d882d5
     struct mroute_addr addr;
512cda46
 
81d882d5
     addr.len = 16;
     addr.type = MR_ADDR_IPV6;
     addr.netbits = 0;
     addr.v6.addr = a6;
512cda46
 
81d882d5
     if (netbits >= 0)
512cda46
     {
81d882d5
         addr.type |= MR_WITH_NETBITS;
         addr.netbits = (uint8_t) netbits;
         mroute_addr_mask_host_bits( &addr );
512cda46
     }
 
81d882d5
     {
         struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
512cda46
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
         if (management && owner)
         {
             management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
         }
512cda46
 #endif
81d882d5
         return owner;
     }
512cda46
 }
 
6fbf66fa
 /*
  * A new client has connected, add routes (server -> client)
  * to internal routing table.
  */
 static void
81d882d5
 multi_add_iroutes(struct multi_context *m,
                   struct multi_instance *mi)
 {
     struct gc_arena gc = gc_new();
     const struct iroute *ir;
     const struct iroute_ipv6 *ir6;
     if (TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN)
     {
         mi->did_iroutes = true;
         for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
         {
             if (ir->netbits >= 0)
             {
                 msg(D_MULTI_LOW, "MULTI: internal route %s/%d -> %s",
                     print_in_addr_t(ir->network, 0, &gc),
                     ir->netbits,
                     multi_instance_string(mi, false, &gc));
             }
             else
             {
                 msg(D_MULTI_LOW, "MULTI: internal route %s -> %s",
                     print_in_addr_t(ir->network, 0, &gc),
                     multi_instance_string(mi, false, &gc));
             }
 
             mroute_helper_add_iroute46(m->route_helper, ir->netbits);
 
             multi_learn_in_addr_t(m, mi, ir->network, ir->netbits, false);
         }
         for (ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next)
         {
             msg(D_MULTI_LOW, "MULTI: internal route %s/%d -> %s",
                 print_in6_addr(ir6->network, 0, &gc),
                 ir6->netbits,
                 multi_instance_string(mi, false, &gc));
 
             mroute_helper_add_iroute46(m->route_helper, ir6->netbits);
 
             multi_learn_in6_addr(m, mi, ir6->network, ir6->netbits, false);
         }
     }
     gc_free(&gc);
6fbf66fa
 }
 
 /*
  * Given an instance (new_mi), delete all other instances which use the
  * same common name.
  */
 static void
81d882d5
 multi_delete_dup(struct multi_context *m, struct multi_instance *new_mi)
 {
     if (new_mi)
     {
         const char *new_cn = tls_common_name(new_mi->context.c2.tls_multi, true);
         if (new_cn)
         {
             struct hash_iterator hi;
             struct hash_element *he;
             int count = 0;
 
             hash_iterator_init(m->iter, &hi);
             while ((he = hash_iterator_next(&hi)))
             {
                 struct multi_instance *mi = (struct multi_instance *) he->value;
                 if (mi != new_mi && !mi->halt)
                 {
                     const char *cn = tls_common_name(mi->context.c2.tls_multi, true);
                     if (cn && !strcmp(cn, new_cn))
                     {
                         mi->did_iter = false;
                         multi_close_instance(m, mi, false);
                         hash_iterator_delete_element(&hi);
                         ++count;
                     }
                 }
             }
             hash_iterator_free(&hi);
 
             if (count)
             {
                 msg(D_MULTI_LOW, "MULTI: new connection by client '%s' will cause previous active sessions by this client to be dropped.  Remember to use the --duplicate-cn option if you want multiple clients using the same certificate or username to concurrently connect.", new_cn);
             }
         }
6fbf66fa
     }
 }
 
3a957aae
 static void
81d882d5
 check_stale_routes(struct multi_context *m)
3a957aae
 {
 
81d882d5
     struct gc_arena gc = gc_new();
     struct hash_iterator hi;
     struct hash_element *he;
3a957aae
 
81d882d5
     dmsg(D_MULTI_DEBUG, "MULTI: Checking stale routes");
     hash_iterator_init_range(m->vhash, &hi, 0, hash_n_buckets(m->vhash));
     while ((he = hash_iterator_next(&hi)) != NULL)
3a957aae
     {
81d882d5
         struct multi_route *r = (struct multi_route *) he->value;
         if (multi_route_defined(m, r) && difftime(now, r->last_reference) >= m->top.options.stale_routes_ageing_time)
3a957aae
         {
81d882d5
             dmsg(D_MULTI_DEBUG, "MULTI: Deleting stale route for address '%s'",
                  mroute_addr_print(&r->addr, &gc));
             learn_address_script(m, NULL, "delete", &r->addr);
             multi_route_del(r);
             hash_iterator_delete_element(&hi);
3a957aae
         }
     }
81d882d5
     hash_iterator_free(&hi);
     gc_free(&gc);
3a957aae
 }
 
6fbf66fa
 /*
3c7f2f55
  * Ensure that endpoint to be pushed to client
  * complies with --ifconfig-push-constraint directive.
  */
 static bool
81d882d5
 ifconfig_push_constraint_satisfied(const struct context *c)
3c7f2f55
 {
81d882d5
     const struct options *o = &c->options;
     if (o->push_ifconfig_constraint_defined && c->c2.push_ifconfig_defined)
     {
         return (o->push_ifconfig_constraint_netmask & c->c2.push_ifconfig_local) == o->push_ifconfig_constraint_network;
     }
     else
     {
         return true;
     }
3c7f2f55
 }
 
 /*
6fbf66fa
  * Select a virtual address for a new client instance.
  * Use an --ifconfig-push directive, if given (static IP).
81d882d5
  * Otherwise use an --ifconfig-pool address (dynamic IP).
6fbf66fa
  */
 static void
81d882d5
 multi_select_virtual_addr(struct multi_context *m, struct multi_instance *mi)
 {
     struct gc_arena gc = gc_new();
 
     /*
      * If ifconfig addresses were set by dynamic config file,
      * release pool addresses, otherwise keep them.
      */
     if (mi->context.options.push_ifconfig_defined)
     {
         /* ifconfig addresses were set statically,
          * release dynamic allocation */
         if (mi->vaddr_handle >= 0)
         {
             ifconfig_pool_release(m->ifconfig_pool, mi->vaddr_handle, true);
             mi->vaddr_handle = -1;
         }
 
         mi->context.c2.push_ifconfig_defined = true;
         mi->context.c2.push_ifconfig_local = mi->context.options.push_ifconfig_local;
         mi->context.c2.push_ifconfig_remote_netmask = mi->context.options.push_ifconfig_remote_netmask;
         mi->context.c2.push_ifconfig_local_alias = mi->context.options.push_ifconfig_local_alias;
 
         /* the current implementation does not allow "static IPv4, pool IPv6",
          * (see below) so issue a warning if that happens - don't break the
          * session, though, as we don't even know if this client WANTS IPv6
          */
         if (mi->context.options.ifconfig_ipv6_pool_defined
             && !mi->context.options.push_ifconfig_ipv6_defined)
         {
             msg( M_INFO, "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 (m->ifconfig_pool && mi->vaddr_handle < 0) /* otherwise, choose a pool address */
     {
         in_addr_t local = 0, remote = 0;
         struct in6_addr remote_ipv6;
         const char *cn = NULL;
 
         if (!mi->context.options.duplicate_cn)
         {
             cn = tls_common_name(mi->context.c2.tls_multi, true);
         }
 
         CLEAR(remote_ipv6);
         mi->vaddr_handle = ifconfig_pool_acquire(m->ifconfig_pool, &local, &remote, &remote_ipv6, cn);
         if (mi->vaddr_handle >= 0)
         {
             const int tunnel_type = TUNNEL_TYPE(mi->context.c1.tuntap);
             const int tunnel_topology = TUNNEL_TOPOLOGY(mi->context.c1.tuntap);
 
             msg( M_INFO, "MULTI_sva: pool returned IPv4=%s, IPv6=%s",
                  print_in_addr_t( remote, 0, &gc ),
                  (mi->context.options.ifconfig_ipv6_pool_defined
                   ? print_in6_addr( remote_ipv6, 0, &gc )
                   : "(Not enabled)") );
 
             /* set push_ifconfig_remote_netmask from pool ifconfig address(es) */
             mi->context.c2.push_ifconfig_local = remote;
             if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET))
             {
                 mi->context.c2.push_ifconfig_remote_netmask = mi->context.options.ifconfig_pool_netmask;
                 if (!mi->context.c2.push_ifconfig_remote_netmask)
                 {
                     mi->context.c2.push_ifconfig_remote_netmask = mi->context.c1.tuntap->remote_netmask;
                 }
             }
             else if (tunnel_type == DEV_TYPE_TUN)
             {
                 if (tunnel_topology == TOP_P2P)
                 {
                     mi->context.c2.push_ifconfig_remote_netmask = mi->context.c1.tuntap->local;
                 }
                 else if (tunnel_topology == TOP_NET30)
                 {
                     mi->context.c2.push_ifconfig_remote_netmask = local;
                 }
             }
 
             if (mi->context.c2.push_ifconfig_remote_netmask)
             {
                 mi->context.c2.push_ifconfig_defined = true;
             }
             else
             {
                 msg(D_MULTI_ERRORS, "MULTI: no --ifconfig-pool netmask parameter is available to push to %s",
                     multi_instance_string(mi, false, &gc));
             }
 
             if (mi->context.options.ifconfig_ipv6_pool_defined)
             {
                 mi->context.c2.push_ifconfig_ipv6_local = remote_ipv6;
                 mi->context.c2.push_ifconfig_ipv6_remote =
                     mi->context.c1.tuntap->local_ipv6;
                 mi->context.c2.push_ifconfig_ipv6_netbits =
                     mi->context.options.ifconfig_ipv6_netbits;
                 mi->context.c2.push_ifconfig_ipv6_defined = true;
             }
         }
         else
         {
             msg(D_MULTI_ERRORS, "MULTI: no free --ifconfig-pool addresses are available");
         }
     }
 
     /* IPv6 push_ifconfig is a bit problematic - since IPv6 shares the
      * pool handling with IPv4, the combination "static IPv4, dynamic IPv6"
      * will fail (because no pool will be allocated in this case).
      * OTOH, this doesn't make too much sense in reality - and the other
      * way round ("dynamic IPv4, static IPv6") or "both static" makes sense
      * -> and so it's implemented right now
      */
     if (mi->context.options.push_ifconfig_ipv6_defined)
     {
         mi->context.c2.push_ifconfig_ipv6_local =
             mi->context.options.push_ifconfig_ipv6_local;
         mi->context.c2.push_ifconfig_ipv6_remote =
             mi->context.options.push_ifconfig_ipv6_remote;
         mi->context.c2.push_ifconfig_ipv6_netbits =
             mi->context.options.push_ifconfig_ipv6_netbits;
         mi->context.c2.push_ifconfig_ipv6_defined = true;
 
         msg( M_INFO, "MULTI_sva: push_ifconfig_ipv6 %s/%d",
              print_in6_addr( mi->context.c2.push_ifconfig_ipv6_local, 0, &gc ),
              mi->context.c2.push_ifconfig_ipv6_netbits );
     }
 
     gc_free(&gc);
6fbf66fa
 }
 
 /*
  * Set virtual address environmental variables.
  */
 static void
81d882d5
 multi_set_virtual_addr_env(struct multi_context *m, struct multi_instance *mi)
 {
     setenv_del(mi->context.c2.es, "ifconfig_pool_local_ip");
     setenv_del(mi->context.c2.es, "ifconfig_pool_remote_ip");
     setenv_del(mi->context.c2.es, "ifconfig_pool_netmask");
 
     if (mi->context.c2.push_ifconfig_defined)
     {
         const int tunnel_type = TUNNEL_TYPE(mi->context.c1.tuntap);
         const int tunnel_topology = TUNNEL_TOPOLOGY(mi->context.c1.tuntap);
 
         setenv_in_addr_t(mi->context.c2.es,
                          "ifconfig_pool_remote_ip",
                          mi->context.c2.push_ifconfig_local,
                          SA_SET_IF_NONZERO);
 
         if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET))
         {
             setenv_in_addr_t(mi->context.c2.es,
                              "ifconfig_pool_netmask",
                              mi->context.c2.push_ifconfig_remote_netmask,
                              SA_SET_IF_NONZERO);
         }
         else if (tunnel_type == DEV_TYPE_TUN)
         {
             setenv_in_addr_t(mi->context.c2.es,
                              "ifconfig_pool_local_ip",
                              mi->context.c2.push_ifconfig_remote_netmask,
                              SA_SET_IF_NONZERO);
         }
     }
 
     setenv_del(mi->context.c2.es, "ifconfig_pool_local_ip6");
     setenv_del(mi->context.c2.es, "ifconfig_pool_remote_ip6");
     setenv_del(mi->context.c2.es, "ifconfig_pool_ip6_netbits");
 
     if (mi->context.c2.push_ifconfig_ipv6_defined)
     {
         setenv_in6_addr(mi->context.c2.es,
                         "ifconfig_pool_remote",
                         &mi->context.c2.push_ifconfig_ipv6_local,
                         SA_SET_IF_NONZERO);
         setenv_in6_addr(mi->context.c2.es,
                         "ifconfig_pool_local",
                         &mi->context.c2.push_ifconfig_ipv6_remote,
                         SA_SET_IF_NONZERO);
         setenv_int(mi->context.c2.es,
                    "ifconfig_pool_ip6_netbits",
                    mi->context.c2.push_ifconfig_ipv6_netbits);
a8f8b926
     }
6fbf66fa
 }
 
 /*
3c7f2f55
  * Called after client-connect script is called
6fbf66fa
  */
 static void
81d882d5
 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)
6fbf66fa
 {
81d882d5
     /* Did script generate a dynamic config file? */
b7bea782
     if (platform_test_file(dc_file))
6fbf66fa
     {
81d882d5
         options_server_import(&mi->context.options,
                               dc_file,
                               D_IMPORT_ERRORS|M_OPTERR,
                               option_permissions_mask,
                               option_types_found,
                               mi->context.c2.es);
6fbf66fa
 
81d882d5
         /*
          * If the --client-connect script generates a config file
          * with an --ifconfig-push directive, it will override any
          * --ifconfig-push directive from the --client-config-dir
          * directory or any --ifconfig-pool dynamic address.
          */
         multi_select_virtual_addr(m, mi);
         multi_set_virtual_addr_env(m, mi);
6fbf66fa
     }
 }
 
3c7f2f55
 #ifdef ENABLE_PLUGIN
 
 /*
  * Called after client-connect plug-in is called
  */
 static void
81d882d5
 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;
 
     plugin_return_get_column(pr, &config, "config");
 
     /* Did script generate a dynamic config file? */
     if (plugin_return_defined(&config))
     {
         int i;
         for (i = 0; i < config.n; ++i)
         {
             if (config.list[i] && config.list[i]->value)
             {
                 options_string_import(&mi->context.options,
                                       config.list[i]->value,
                                       D_IMPORT_ERRORS|M_OPTERR,
                                       option_permissions_mask,
                                       option_types_found,
                                       mi->context.c2.es);
             }
         }
 
         /*
          * If the --client-connect script generates a config file
          * with an --ifconfig-push directive, it will override any
          * --ifconfig-push directive from the --client-config-dir
          * directory or any --ifconfig-pool dynamic address.
          */
         multi_select_virtual_addr(m, mi);
         multi_set_virtual_addr_env(m, mi);
3c7f2f55
     }
 }
 
81d882d5
 #endif /* ifdef ENABLE_PLUGIN */
3c7f2f55
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
 
 /*
  * Called to load management-derived client-connect config
  */
 static void
81d882d5
 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)
     {
         struct buffer_entry *be;
 
         for (be = 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,
                                   option_types_found,
                                   mi->context.c2.es);
         }
 
         /*
          * If the --client-connect script generates a config file
          * with an --ifconfig-push directive, it will override any
          * --ifconfig-push directive from the --client-config-dir
          * directory or any --ifconfig-pool dynamic address.
          */
         multi_select_virtual_addr(m, mi);
         multi_set_virtual_addr_env(m, mi);
90efcacb
     }
 }
 
81d882d5
 #endif /* ifdef MANAGEMENT_DEF_AUTH */
90efcacb
 
a783c400
 static void
81d882d5
 multi_client_connect_setenv(struct multi_context *m,
                             struct multi_instance *mi)
a783c400
 {
81d882d5
     struct gc_arena gc = gc_new();
a783c400
 
81d882d5
     /* setenv incoming cert common name for script */
     setenv_str(mi->context.c2.es, "common_name", tls_common_name(mi->context.c2.tls_multi, true));
a783c400
 
81d882d5
     /* setenv client real IP address */
     setenv_trusted(mi->context.c2.es, get_link_socket_info(&mi->context));
a783c400
 
81d882d5
     /* setenv client virtual IP address */
     multi_set_virtual_addr_env(m, mi);
a783c400
 
81d882d5
     /* setenv connection time */
     {
         const char *created_ascii = time_string(mi->created, 0, false, &gc);
         setenv_str(mi->context.c2.es, "time_ascii", created_ascii);
6c69693a
         setenv_long_long(mi->context.c2.es, "time_unix", mi->created);
81d882d5
     }
a783c400
 
81d882d5
     gc_free(&gc);
a783c400
 }
 
6fbf66fa
 /*
  * 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
81d882d5
 multi_connection_established(struct multi_context *m, struct multi_instance *mi)
 {
     if (tls_authentication_status(mi->context.c2.tls_multi, 0) == TLS_AUTHENTICATION_SUCCEEDED)
     {
         struct gc_arena gc = gc_new();
         unsigned int option_types_found = 0;
6fbf66fa
 
81d882d5
         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;
3c7f2f55
 
81d882d5
         int cc_succeeded = true; /* client connect script status */
         int cc_succeeded_count = 0;
495e3cec
 
81d882d5
         ASSERT(mi->context.c1.tuntap);
3c7f2f55
 
81d882d5
         /* 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);
6fbf66fa
 
81d882d5
         /* generate a msg() prefix for this client instance */
         generate_prefix(mi);
6fbf66fa
 
81d882d5
         /* delete instances of previous clients with same common-name */
         if (!mi->context.options.duplicate_cn)
         {
             multi_delete_dup(m, mi);
         }
 
         /* reset pool handle to null */
         mi->vaddr_handle = -1;
 
         /*
          * Try to source a dynamic config file from the
          * --client-config-dir directory.
          */
         if (mi->context.options.client_config_dir)
         {
             const char *ccd_file;
 
b7bea782
             ccd_file = platform_gen_path(mi->context.options.client_config_dir,
                                          tls_common_name(mi->context.c2.tls_multi,
                                                          false),
                                          &gc);
81d882d5
 
             /* try common-name file */
b7bea782
             if (platform_test_file(ccd_file))
81d882d5
             {
                 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 */
             {
b7bea782
                 ccd_file = platform_gen_path(mi->context.options.client_config_dir,
                                              CCD_DEFAULT,
                                              &gc);
81d882d5
 
b7bea782
                 if (platform_test_file(ccd_file))
81d882d5
                 {
                     options_server_import(&mi->context.options,
                                           ccd_file,
                                           D_IMPORT_ERRORS|M_OPTERR,
                                           option_permissions_mask,
                                           &option_types_found,
                                           mi->context.c2.es);
                 }
             }
         }
 
         /*
          * 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);
 
 #ifdef ENABLE_PLUGIN
         /*
          * Call client-connect plug-in.
          */
 
         /* 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();
b7bea782
             const char *dc_file = platform_create_temp_file(mi->context.options.tmp_dir,
                                                             "cc", &gc);
81d882d5
 
             if (!dc_file)
             {
                 cc_succeeded = false;
                 goto script_depr_failed;
             }
 
             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;
             }
 
             if (!platform_unlink(dc_file))
             {
                 msg(D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s",
                     dc_file);
             }
 
 script_depr_failed:
             argv_reset(&argv);
         }
 
         /* 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;
 
             plugin_return_init(&pr);
 
             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;
             }
 
             plugin_return_free(&pr);
         }
 #endif /* ifdef ENABLE_PLUGIN */
 
         /*
          * Run --client-connect script.
          */
         if (mi->context.options.client_connect_script && cc_succeeded)
         {
             struct argv argv = argv_new();
             const char *dc_file = NULL;
 
             setenv_str(mi->context.c2.es, "script_type", "client-connect");
 
b7bea782
             dc_file = platform_create_temp_file(mi->context.options.tmp_dir,
                                                 "cc", &gc);
81d882d5
             if (!dc_file)
             {
                 cc_succeeded = false;
                 goto script_failed;
             }
 
             argv_parse_cmd(&argv, mi->context.options.client_connect_script);
             argv_printf_cat(&argv, "%s", dc_file);
 
             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;
             }
 
             if (!platform_unlink(dc_file))
             {
                 msg(D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s",
                     dc_file);
             }
 
 script_failed:
             argv_reset(&argv);
         }
 
         /*
          * Check for client-connect script left by management interface client
          */
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
         if (cc_succeeded && mi->cc_config)
         {
             multi_client_connect_mda(m, mi, mi->cc_config, option_permissions_mask, &option_types_found);
             ++cc_succeeded_count;
         }
90efcacb
 #endif
 
81d882d5
         /*
          * Check for "disable" directive in client-config-dir file
          * or config file generated by --client-connect script.
          */
         if (mi->context.options.disable)
         {
             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;
0d1a75bf
 
 #ifdef ENABLE_ASYNC_PUSH
81d882d5
             /* authentication complete, send push reply */
             if (mi->context.c2.push_request_received)
             {
                 process_incoming_push_request(&mi->context);
             }
0d1a75bf
 #endif
81d882d5
         }
         else
         {
             /* set context-level authentication flag */
             mi->context.c2.context_auth = cc_succeeded_count ? CAS_PARTIAL : CAS_FAILED;
         }
6fbf66fa
 
81d882d5
         /* set flag so we don't get called again */
         mi->connection_established_flag = true;
6fbf66fa
 
81d882d5
         /* increment number of current authenticated clients */
         ++m->n_clients;
         update_mstat_n_clients(m->n_clients);
         --mi->n_clients_delta;
4fb4ce12
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
         if (management)
         {
             management_connection_established(management, &mi->context.c2.mda_context, mi->context.c2.es);
         }
90efcacb
 #endif
 
81d882d5
         gc_free(&gc);
6fbf66fa
     }
 
81d882d5
     /*
      * Reply now to client's PUSH_REQUEST query
      */
     mi->context.c2.push_reply_deferred = false;
6fbf66fa
 }
 
0d1a75bf
 #ifdef ENABLE_ASYNC_PUSH
 /*
  * Called when inotify event is fired, which happens when acf file is closed or deleted.
  * Continues authentication and sends push_reply.
  */
 void
81d882d5
 multi_process_file_closed(struct multi_context *m, const unsigned int mpp_flags)
 {
     char buffer[INOTIFY_EVENT_BUFFER_SIZE];
     size_t buffer_i = 0;
     int r = read(m->top.c2.inotify_fd, buffer, INOTIFY_EVENT_BUFFER_SIZE);
 
     while (buffer_i < r)
     {
         /* parse inotify events */
         struct inotify_event *pevent = (struct inotify_event *) &buffer[buffer_i];
         size_t event_size = sizeof(struct inotify_event) + pevent->len;
         buffer_i += event_size;
 
         msg(D_MULTI_DEBUG, "MULTI: modified fd %d, mask %d", pevent->wd, pevent->mask);
 
         struct multi_instance *mi = hash_lookup(m->inotify_watchers, (void *) (unsigned long) pevent->wd);
 
         if (pevent->mask & IN_CLOSE_WRITE)
         {
             if (mi)
             {
                 /* continue authentication and send push_reply */
                 multi_process_post(m, mi, mpp_flags);
             }
             else
             {
                 msg(D_MULTI_ERRORS, "MULTI: multi_instance not found!");
             }
         }
         else if (pevent->mask & IN_IGNORED)
         {
             /* this event is _always_ fired when watch is removed or file is deleted */
             if (mi)
             {
                 hash_remove(m->inotify_watchers, (void *) (unsigned long) pevent->wd);
                 mi->inotify_watch = -1;
             }
         }
         else
         {
             msg(D_MULTI_ERRORS, "MULTI: unknown mask %d", pevent->mask);
         }
0d1a75bf
     }
 }
81d882d5
 #endif /* ifdef ENABLE_ASYNC_PUSH */
0d1a75bf
 
6fbf66fa
 /*
  * Add a mbuf buffer to a particular
  * instance.
  */
 void
81d882d5
 multi_add_mbuf(struct multi_context *m,
                struct multi_instance *mi,
                struct mbuf_buffer *mb)
6fbf66fa
 {
81d882d5
     if (multi_output_queue_ready(m, mi))
6fbf66fa
     {
81d882d5
         struct mbuf_item item;
         item.buffer = mb;
         item.instance = mi;
         mbuf_add_item(m->mbuf, &item);
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         msg(D_MULTI_DROPPED, "MULTI: packet dropped due to output saturation (multi_add_mbuf)");
6fbf66fa
     }
 }
 
 /*
  * Add a packet to a client instance output queue.
  */
 static inline void
81d882d5
 multi_unicast(struct multi_context *m,
               const struct buffer *buf,
               struct multi_instance *mi)
6fbf66fa
 {
81d882d5
     struct mbuf_buffer *mb;
6fbf66fa
 
81d882d5
     if (BLEN(buf) > 0)
6fbf66fa
     {
81d882d5
         mb = mbuf_alloc_buf(buf);
         mb->flags = MF_UNICAST;
         multi_add_mbuf(m, mi, mb);
         mbuf_free_buf(mb);
6fbf66fa
     }
 }
 
 /*
  * Broadcast a packet to all clients.
  */
90efcacb
 static void
81d882d5
 multi_bcast(struct multi_context *m,
             const struct buffer *buf,
             const struct multi_instance *sender_instance,
             const struct mroute_addr *sender_addr)
6fbf66fa
 {
81d882d5
     struct hash_iterator hi;
     struct hash_element *he;
     struct multi_instance *mi;
     struct mbuf_buffer *mb;
6fbf66fa
 
81d882d5
     if (BLEN(buf) > 0)
6fbf66fa
     {
81d882d5
         perf_push(PERF_MULTI_BCAST);
6fbf66fa
 #ifdef MULTI_DEBUG_EVENT_LOOP
81d882d5
         printf("BCAST len=%d\n", BLEN(buf));
6fbf66fa
 #endif
81d882d5
         mb = mbuf_alloc_buf(buf);
         hash_iterator_init(m->iter, &hi);
 
         while ((he = hash_iterator_next(&hi)))
         {
             mi = (struct multi_instance *) he->value;
             if (mi != sender_instance && !mi->halt)
             {
90efcacb
 #ifdef ENABLE_PF
81d882d5
                 if (sender_instance)
                 {
                     if (!pf_c2c_test(&sender_instance->context, &mi->context, "bcast_c2c"))
                     {
                         msg(D_PF_DROPPED_BCAST, "PF: client[%s] -> client[%s] packet dropped by BCAST packet filter",
                             mi_prefix(sender_instance),
                             mi_prefix(mi));
                         continue;
                     }
                 }
                 if (sender_addr)
                 {
                     if (!pf_addr_test(&mi->context, sender_addr, "bcast_src_addr"))
                     {
                         struct gc_arena gc = gc_new();
                         msg(D_PF_DROPPED_BCAST, "PF: addr[%s] -> client[%s] packet dropped by BCAST packet filter",
                             mroute_addr_print_ex(sender_addr, MAPF_SHOW_ARP, &gc),
                             mi_prefix(mi));
                         gc_free(&gc);
                         continue;
                     }
                 }
 #endif /* ifdef ENABLE_PF */
                 multi_add_mbuf(m, mi, mb);
             }
         }
6fbf66fa
 
81d882d5
         hash_iterator_free(&hi);
         mbuf_free_buf(mb);
         perf_pop();
6fbf66fa
     }
 }
 
 /*
  * Given a time delta, indicating that we wish to be
  * awoken by the scheduler at time now + delta, figure
  * a sigma parameter (in microseconds) that represents
  * a sort of fuzz factor around delta, so that we're
  * really telling the scheduler to wake us up any time
  * between now + delta - sigma and now + delta + sigma.
  *
  * The sigma parameter helps the scheduler to run more efficiently.
  * Sigma should be no larger than TV_WITHIN_SIGMA_MAX_USEC
  */
 static inline unsigned int
81d882d5
 compute_wakeup_sigma(const struct timeval *delta)
6fbf66fa
 {
81d882d5
     if (delta->tv_sec < 1)
6fbf66fa
     {
81d882d5
         /* if < 1 sec, fuzz = # of microseconds / 8 */
         return delta->tv_usec >> 3;
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         /* if < 10 minutes, fuzz = 13.1% of timeout */
         if (delta->tv_sec < 600)
         {
             return delta->tv_sec << 17;
         }
         else
         {
             return 120000000; /* if >= 10 minutes, fuzz = 2 minutes */
         }
6fbf66fa
     }
 }
 
a345b41e
 static void
81d882d5
 multi_schedule_context_wakeup(struct multi_context *m, struct multi_instance *mi)
a345b41e
 {
81d882d5
     /* calculate an absolute wakeup time */
     ASSERT(!openvpn_gettimeofday(&mi->wakeup, NULL));
     tv_add(&mi->wakeup, &mi->context.c2.timeval);
a345b41e
 
81d882d5
     /* tell scheduler to wake us up at some point in the future */
     schedule_add_entry(m->schedule,
                        (struct schedule_entry *) mi,
                        &mi->wakeup,
                        compute_wakeup_sigma(&mi->context.c2.timeval));
a345b41e
 }
 
6fbf66fa
 /*
  * Figure instance-specific timers, convert
  * earliest to absolute time in mi->wakeup,
  * call scheduler with our future wakeup time.
  *
  * Also close context on signal.
  */
 bool
81d882d5
 multi_process_post(struct multi_context *m, struct multi_instance *mi, const unsigned int flags)
6fbf66fa
 {
81d882d5
     bool ret = true;
6fbf66fa
 
81d882d5
     if (!IS_SIG(&mi->context) && ((flags & MPP_PRE_SELECT) || ((flags & MPP_CONDITIONAL_PRE_SELECT) && !ANY_OUT(&mi->context))))
6fbf66fa
     {
0d1a75bf
 #if defined(ENABLE_ASYNC_PUSH) && defined(ENABLE_DEF_AUTH)
81d882d5
         bool was_authenticated = false;
         struct key_state *ks = NULL;
         if (mi->context.c2.tls_multi)
0d1a75bf
         {
81d882d5
             ks = &mi->context.c2.tls_multi->session[TM_ACTIVE].key[KS_PRIMARY];
             was_authenticated = ks->authenticated;
0d1a75bf
         }
 #endif
 
81d882d5
         /* figure timeouts and fetch possible outgoing
          * to_link packets (such as ping or TLS control) */
         pre_select(&mi->context);
6fbf66fa
 
0d1a75bf
 #if defined(ENABLE_ASYNC_PUSH) && defined(ENABLE_DEF_AUTH)
81d882d5
         if (ks && ks->auth_control_file && ks->auth_deferred && !was_authenticated)
         {
             /* watch acf file */
             long watch_descriptor = inotify_add_watch(m->top.c2.inotify_fd, ks->auth_control_file, IN_CLOSE_WRITE | IN_ONESHOT);
             if (watch_descriptor >= 0)
             {
                 if (mi->inotify_watch != -1)
                 {
                     hash_remove(m->inotify_watchers, (void *) (unsigned long)mi->inotify_watch);
                 }
                 hash_add(m->inotify_watchers, (const uintptr_t *)watch_descriptor, mi, true);
                 mi->inotify_watch = watch_descriptor;
             }
             else
             {
56b396dc
                 msg(M_NONFATAL | M_ERRNO, "MULTI: inotify_add_watch error");
81d882d5
             }
         }
0d1a75bf
 #endif
6fbf66fa
 
81d882d5
         if (!IS_SIG(&mi->context))
         {
             /* connection is "established" when SSL/TLS key negotiation succeeds
              * and (if specified) auth user/pass succeeds */
             if (!mi->connection_established_flag && CONNECTION_ESTABLISHED(&mi->context))
             {
                 multi_connection_established(m, mi);
             }
 
             /* tell scheduler to wake us up at some point in the future */
             multi_schedule_context_wakeup(m, mi);
         }
6fbf66fa
     }
 
81d882d5
     if (IS_SIG(&mi->context))
6fbf66fa
     {
81d882d5
         if (flags & MPP_CLOSE_ON_SIGNAL)
         {
             multi_close_instance_on_signal(m, mi);
             ret = false;
         }
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         /* continue to pend on output? */
         multi_set_pending(m, ANY_OUT(&mi->context) ? mi : NULL);
6fbf66fa
 
 #ifdef MULTI_DEBUG_EVENT_LOOP
06ad53e0
         printf("POST %s[%d] to=%d lo=%d/%d w=%"PRIi64"/%ld\n",
81d882d5
                id(mi),
                (int) (mi == m->pending),
                mi ? mi->context.c2.to_tun.len : -1,
                mi ? mi->context.c2.to_link.len : -1,
                (mi && mi->context.c2.fragment) ? mi->context.c2.fragment->outgoing.len : -1,
06ad53e0
                (int64_t)mi->context.c2.timeval.tv_sec,
31b5c0e9
                (long)mi->context.c2.timeval.tv_usec);
6fbf66fa
 #endif
     }
 
81d882d5
     if ((flags & MPP_RECORD_TOUCH) && m->mpp_touched)
     {
         *m->mpp_touched = mi;
     }
6fbf66fa
 
81d882d5
     return ret;
6fbf66fa
 }
 
81d882d5
 void
 multi_process_float(struct multi_context *m, struct multi_instance *mi)
65eedc35
 {
81d882d5
     struct mroute_addr real;
     struct hash *hash = m->hash;
     struct gc_arena gc = gc_new();
65eedc35
 
81d882d5
     if (!mroute_extract_openvpn_sockaddr(&real, &m->top.c2.from.dest, true))
     {
         goto done;
     }
65eedc35
 
81d882d5
     const uint32_t hv = hash_value(hash, &real);
     struct hash_bucket *bucket = hash_bucket(hash, hv);
65eedc35
 
81d882d5
     /* make sure that we don't float to an address taken by another client */
     struct hash_element *he = hash_lookup_fast(hash, bucket, &real, hv);
     if (he)
65eedc35
     {
81d882d5
         struct multi_instance *ex_mi = (struct multi_instance *) he->value;
65eedc35
 
81d882d5
         struct tls_multi *m1 = mi->context.c2.tls_multi;
         struct tls_multi *m2 = ex_mi->context.c2.tls_multi;
0c0c178a
 
81d882d5
         /* do not float if target address is taken by client with another cert */
         if (!cert_hash_compare(m1->locked_cert_hash_set, m2->locked_cert_hash_set))
         {
             msg(D_MULTI_LOW, "Disallow float to an address taken by another client %s",
                 multi_instance_string(ex_mi, false, &gc));
65eedc35
 
81d882d5
             mi->context.c2.buf.len = 0;
65eedc35
 
81d882d5
             goto done;
         }
65eedc35
 
81d882d5
         msg(D_MULTI_MEDIUM, "closing instance %s", multi_instance_string(ex_mi, false, &gc));
         multi_close_instance(m, ex_mi, false);
65eedc35
     }
 
81d882d5
     msg(D_MULTI_MEDIUM, "peer %" PRIu32 " (%s) floated from %s to %s",
         mi->context.c2.tls_multi->peer_id,
         tls_common_name(mi->context.c2.tls_multi, false),
         mroute_addr_print(&mi->real, &gc),
         print_link_socket_actual(&m->top.c2.from, &gc));
65eedc35
 
c5938859
     /* remove old address from hash table before changing address */
81d882d5
     ASSERT(hash_remove(m->hash, &mi->real));
     ASSERT(hash_remove(m->iter, &mi->real));
c5938859
 
65eedc35
     /* change external network address of the remote peer */
     mi->real = real;
81d882d5
     generate_prefix(mi);
65eedc35
 
     mi->context.c2.from = m->top.c2.from;
     mi->context.c2.to_link_addr = &mi->context.c2.from;
 
     /* inherit parent link_socket and link_socket_info */
     mi->context.c2.link_socket = m->top.c2.link_socket;
     mi->context.c2.link_socket_info->lsa->actual = m->top.c2.from;
 
81d882d5
     tls_update_remote_addr(mi->context.c2.tls_multi, &mi->context.c2.from);
65eedc35
 
81d882d5
     ASSERT(hash_add(m->hash, &mi->real, mi, false));
     ASSERT(hash_add(m->iter, &mi->real, mi, false));
65eedc35
 
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
     ASSERT(hash_add(m->cid_hash, &mi->context.c2.mda_context.cid, mi, true));
65eedc35
 #endif
 
 done:
81d882d5
     gc_free(&gc);
65eedc35
 }
 
6fbf66fa
 /*
  * Process packets in the TCP/UDP socket -> TUN/TAP interface direction,
  * i.e. client -> server direction.
  */
 bool
81d882d5
 multi_process_incoming_link(struct multi_context *m, struct multi_instance *instance, const unsigned int mpp_flags)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
6fbf66fa
 
81d882d5
     struct context *c;
     struct mroute_addr src, dest;
     unsigned int mroute_flags;
     struct multi_instance *mi;
     bool ret = true;
     bool floated = false;
6fbf66fa
 
81d882d5
     if (m->pending)
     {
         return true;
     }
6fbf66fa
 
81d882d5
     if (!instance)
6fbf66fa
     {
 #ifdef MULTI_DEBUG_EVENT_LOOP
81d882d5
         printf("TCP/UDP -> TUN [%d]\n", BLEN(&m->top.c2.buf));
6fbf66fa
 #endif
81d882d5
         multi_set_pending(m, multi_get_create_instance_udp(m, &floated));
     }
     else
     {
         multi_set_pending(m, instance);
     }
 
     if (m->pending)
     {
         set_prefix(m->pending);
 
         /* get instance context */
         c = &m->pending->context;
 
         if (!instance)
         {
             /* transfer packet pointer from top-level context buffer to instance */
             c->c2.buf = m->top.c2.buf;
 
             /* transfer from-addr from top-level context buffer to instance */
             if (!floated)
             {
                 c->c2.from = m->top.c2.from;
             }
         }
 
         if (BLEN(&c->c2.buf) > 0)
         {
             struct link_socket_info *lsi;
             const uint8_t *orig_buf;
 
             /* decrypt in instance context */
 
             perf_push(PERF_PROC_IN_LINK);
             lsi = get_link_socket_info(c);
             orig_buf = c->c2.buf.data;
             if (process_incoming_link_part1(c, lsi, floated))
             {
                 if (floated)
                 {
                     multi_process_float(m, m->pending);
                 }
 
                 process_incoming_link_part2(c, lsi, orig_buf);
             }
             perf_pop();
 
             if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TUN)
             {
                 /* extract packet source and dest addresses */
                 mroute_flags = mroute_extract_addr_from_packet(&src,
                                                                &dest,
                                                                NULL,
                                                                NULL,
                                                                &c->c2.to_tun,
                                                                DEV_TYPE_TUN);
 
                 /* drop packet if extract failed */
                 if (!(mroute_flags & MROUTE_EXTRACT_SUCCEEDED))
                 {
                     c->c2.to_tun.len = 0;
                 }
                 /* make sure that source address is associated with this client */
                 else if (multi_get_instance_by_virtual_addr(m, &src, true) != m->pending)
                 {
                     /* IPv6 link-local address (fe80::xxx)? */
                     if ( (src.type & MR_ADDR_MASK) == MR_ADDR_IPV6
                          && IN6_IS_ADDR_LINKLOCAL(&src.v6.addr) )
                     {
                         /* do nothing, for now.  TODO: add address learning */
                     }
                     else
                     {
                         msg(D_MULTI_DROPPED, "MULTI: bad source address from client [%s], packet dropped",
                             mroute_addr_print(&src, &gc));
                     }
                     c->c2.to_tun.len = 0;
                 }
                 /* client-to-client communication enabled? */
                 else if (m->enable_c2c)
                 {
                     /* multicast? */
                     if (mroute_flags & MROUTE_EXTRACT_MCAST)
                     {
                         /* for now, treat multicast as broadcast */
                         multi_bcast(m, &c->c2.to_tun, m->pending, NULL);
                     }
                     else /* possible client to client routing */
                     {
                         ASSERT(!(mroute_flags & MROUTE_EXTRACT_BCAST));
                         mi = multi_get_instance_by_virtual_addr(m, &dest, true);
 
                         /* if dest addr is a known client, route to it */
                         if (mi)
                         {
47ae8457
 #ifdef ENABLE_PF
81d882d5
                             if (!pf_c2c_test(c, &mi->context, "tun_c2c"))
                             {
                                 msg(D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TUN packet filter",
                                     mi_prefix(mi));
                             }
                             else
47ae8457
 #endif
81d882d5
                             {
                                 multi_unicast(m, &c->c2.to_tun, mi);
                                 register_activity(c, BLEN(&c->c2.to_tun));
                             }
                             c->c2.to_tun.len = 0;
                         }
                     }
                 }
47ae8457
 #ifdef ENABLE_PF
81d882d5
                 if (c->c2.to_tun.len && !pf_addr_test(c, &dest, "tun_dest_addr"))
                 {
                     msg(D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TUN packet filter",
                         mroute_addr_print_ex(&dest, MAPF_SHOW_ARP, &gc));
                     c->c2.to_tun.len = 0;
                 }
47ae8457
 #endif
81d882d5
             }
             else if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TAP)
             {
90efcacb
 #ifdef ENABLE_PF
81d882d5
                 struct mroute_addr edest;
                 mroute_addr_reset(&edest);
90efcacb
 #endif
81d882d5
                 /* extract packet source and dest addresses */
                 mroute_flags = mroute_extract_addr_from_packet(&src,
                                                                &dest,
                                                                NULL,
90efcacb
 #ifdef ENABLE_PF
81d882d5
                                                                &edest,
90efcacb
 #else
81d882d5
                                                                NULL,
90efcacb
 #endif
81d882d5
                                                                &c->c2.to_tun,
                                                                DEV_TYPE_TAP);
 
                 if (mroute_flags & MROUTE_EXTRACT_SUCCEEDED)
                 {
                     if (multi_learn_addr(m, m->pending, &src, 0) == m->pending)
                     {
                         /* check for broadcast */
                         if (m->enable_c2c)
                         {
                             if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST))
                             {
                                 multi_bcast(m, &c->c2.to_tun, m->pending, NULL);
                             }
                             else /* try client-to-client routing */
                             {
                                 mi = multi_get_instance_by_virtual_addr(m, &dest, false);
 
                                 /* if dest addr is a known client, route to it */
                                 if (mi)
                                 {
90efcacb
 #ifdef ENABLE_PF
81d882d5
                                     if (!pf_c2c_test(c, &mi->context, "tap_c2c"))
                                     {
                                         msg(D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TAP packet filter",
                                             mi_prefix(mi));
                                     }
                                     else
90efcacb
 #endif
81d882d5
                                     {
                                         multi_unicast(m, &c->c2.to_tun, mi);
                                         register_activity(c, BLEN(&c->c2.to_tun));
                                     }
                                     c->c2.to_tun.len = 0;
                                 }
                             }
                         }
90efcacb
 #ifdef ENABLE_PF
81d882d5
                         if (c->c2.to_tun.len && !pf_addr_test(c, &edest, "tap_dest_addr"))
                         {
                             msg(D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TAP packet filter",
                                 mroute_addr_print_ex(&edest, MAPF_SHOW_ARP, &gc));
                             c->c2.to_tun.len = 0;
                         }
90efcacb
 #endif
81d882d5
                     }
                     else
                     {
                         msg(D_MULTI_DROPPED, "MULTI: bad source address from client [%s], packet dropped",
                             mroute_addr_print(&src, &gc));
                         c->c2.to_tun.len = 0;
                     }
                 }
                 else
                 {
                     c->c2.to_tun.len = 0;
                 }
             }
         }
6fbf66fa
 
81d882d5
         /* postprocess and set wakeup */
         ret = multi_process_post(m, m->pending, mpp_flags);
6fbf66fa
 
81d882d5
         clear_prefix();
6fbf66fa
     }
 
81d882d5
     gc_free(&gc);
     return ret;
6fbf66fa
 }
 
 /*
  * Process packets in the TUN/TAP interface -> TCP/UDP socket direction,
  * i.e. server -> client direction.
  */
 bool
81d882d5
 multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     bool ret = true;
6fbf66fa
 
81d882d5
     if (BLEN(&m->top.c2.buf) > 0)
6fbf66fa
     {
81d882d5
         unsigned int mroute_flags;
         struct mroute_addr src, dest;
         const int dev_type = TUNNEL_TYPE(m->top.c1.tuntap);
6fbf66fa
 
90efcacb
 #ifdef ENABLE_PF
81d882d5
         struct mroute_addr esrc, *e1, *e2;
         if (dev_type == DEV_TYPE_TUN)
         {
             e1 = NULL;
             e2 = &src;
         }
         else
         {
             e1 = e2 = &esrc;
             mroute_addr_reset(&esrc);
         }
90efcacb
 #endif
 
6fbf66fa
 #ifdef MULTI_DEBUG_EVENT_LOOP
81d882d5
         printf("TUN -> TCP/UDP [%d]\n", BLEN(&m->top.c2.buf));
6fbf66fa
 #endif
 
81d882d5
         if (m->pending)
         {
             return true;
         }
6fbf66fa
 
81d882d5
         /*
          * Route an incoming tun/tap packet to
          * the appropriate multi_instance object.
          */
6fbf66fa
 
81d882d5
         mroute_flags = mroute_extract_addr_from_packet(&src,
                                                        &dest,
90efcacb
 #ifdef ENABLE_PF
81d882d5
                                                        e1,
90efcacb
 #else
81d882d5
                                                        NULL,
90efcacb
 #endif
81d882d5
                                                        NULL,
                                                        &m->top.c2.buf,
                                                        dev_type);
 
         if (mroute_flags & MROUTE_EXTRACT_SUCCEEDED)
         {
             struct context *c;
 
             /* broadcast or multicast dest addr? */
             if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST))
             {
                 /* for now, treat multicast as broadcast */
90efcacb
 #ifdef ENABLE_PF
81d882d5
                 multi_bcast(m, &m->top.c2.buf, NULL, e2);
90efcacb
 #else
81d882d5
                 multi_bcast(m, &m->top.c2.buf, NULL, NULL);
90efcacb
 #endif
81d882d5
             }
             else
             {
                 multi_set_pending(m, multi_get_instance_by_virtual_addr(m, &dest, dev_type == DEV_TYPE_TUN));
 
                 if (m->pending)
                 {
                     /* get instance context */
                     c = &m->pending->context;
 
                     set_prefix(m->pending);
6fbf66fa
 
47ae8457
 #ifdef ENABLE_PF
81d882d5
                     if (!pf_addr_test(c, e2, "tun_tap_src_addr"))
                     {
                         msg(D_PF_DROPPED, "PF: addr[%s] -> client packet dropped by packet filter",
                             mroute_addr_print_ex(&src, MAPF_SHOW_ARP, &gc));
                         buf_reset_len(&c->c2.buf);
                     }
                     else
47ae8457
 #endif
81d882d5
                     {
                         if (multi_output_queue_ready(m, m->pending))
                         {
                             /* transfer packet pointer from top-level context buffer to instance */
                             c->c2.buf = m->top.c2.buf;
                         }
                         else
                         {
                             /* drop packet */
                             msg(D_MULTI_DROPPED, "MULTI: packet dropped due to output saturation (multi_process_incoming_tun)");
                             buf_reset_len(&c->c2.buf);
                         }
                     }
 
                     /* encrypt in instance context */
                     process_incoming_tun(c);
 
                     /* postprocess and set wakeup */
                     ret = multi_process_post(m, m->pending, mpp_flags);
 
                     clear_prefix();
                 }
             }
         }
     }
     gc_free(&gc);
     return ret;
6fbf66fa
 }
 
 /*
  * Process a possible client-to-client/bcast/mcast message in the
  * queue.
  */
 struct multi_instance *
81d882d5
 multi_get_queue(struct mbuf_set *ms)
6fbf66fa
 {
81d882d5
     struct mbuf_item item;
6fbf66fa
 
81d882d5
     if (mbuf_extract_item(ms, &item)) /* cleartext IP packet */
6fbf66fa
     {
81d882d5
         unsigned int pip_flags = PIPV4_PASSTOS;
6fbf66fa
 
81d882d5
         set_prefix(item.instance);
         item.instance->context.c2.buf = item.buffer->buf;
         if (item.buffer->flags & MF_UNICAST) /* --mssfix doesn't make sense for broadcast or multicast */
         {
             pip_flags |= PIP_MSSFIX;
         }
         process_ip_header(&item.instance->context, pip_flags, &item.instance->context.c2.buf);
         encrypt_sign(&item.instance->context, true);
         mbuf_free_buf(item.buffer);
6fbf66fa
 
81d882d5
         dmsg(D_MULTI_DEBUG, "MULTI: C2C/MCAST/BCAST");
6fbf66fa
 
81d882d5
         clear_prefix();
         return item.instance;
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         return NULL;
6fbf66fa
     }
 }
 
 /*
  * Called when an I/O wait times out.  Usually means that a particular
  * client instance object needs timer-based service.
  */
 bool
81d882d5
 multi_process_timeout(struct multi_context *m, const unsigned int mpp_flags)
6fbf66fa
 {
81d882d5
     bool ret = true;
6fbf66fa
 
 #ifdef MULTI_DEBUG_EVENT_LOOP
81d882d5
     printf("%s -> TIMEOUT\n", id(m->earliest_wakeup));
6fbf66fa
 #endif
 
81d882d5
     /* instance marked for wakeup? */
     if (m->earliest_wakeup)
6fbf66fa
     {
81d882d5
         if (m->earliest_wakeup == (struct multi_instance *)&m->deferred_shutdown_signal)
         {
             schedule_remove_entry(m->schedule, (struct schedule_entry *) &m->deferred_shutdown_signal);
             throw_signal(m->deferred_shutdown_signal.signal_received);
         }
         else
         {
             set_prefix(m->earliest_wakeup);
             ret = multi_process_post(m, m->earliest_wakeup, mpp_flags);
             clear_prefix();
         }
         m->earliest_wakeup = NULL;
6fbf66fa
     }
81d882d5
     return ret;
6fbf66fa
 }
 
 /*
  * Drop a TUN/TAP outgoing packet..
  */
 void
81d882d5
 multi_process_drop_outgoing_tun(struct multi_context *m, const unsigned int mpp_flags)
6fbf66fa
 {
81d882d5
     struct multi_instance *mi = m->pending;
6fbf66fa
 
81d882d5
     ASSERT(mi);
6fbf66fa
 
81d882d5
     set_prefix(mi);
6fbf66fa
 
81d882d5
     msg(D_MULTI_ERRORS, "MULTI: Outgoing TUN queue full, dropped packet len=%d",
         mi->context.c2.to_tun.len);
6fbf66fa
 
81d882d5
     buf_reset(&mi->context.c2.to_tun);
6fbf66fa
 
81d882d5
     multi_process_post(m, mi, mpp_flags);
     clear_prefix();
6fbf66fa
 }
 
 /*
  * Per-client route quota management
  */
 
 void
81d882d5
 route_quota_exceeded(const struct multi_context *m, const struct multi_instance *mi)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     msg(D_ROUTE_QUOTA, "MULTI ROUTE: route quota (%d) exceeded for %s (see --max-routes-per-client option)",
         mi->context.options.max_routes_per_client,
         multi_instance_string(mi, false, &gc));
     gc_free(&gc);
6fbf66fa
 }
 
 #ifdef ENABLE_DEBUG
 /*
  * Flood clients with random packets
  */
 static void
81d882d5
 gremlin_flood_clients(struct multi_context *m)
6fbf66fa
 {
81d882d5
     const int level = GREMLIN_PACKET_FLOOD_LEVEL(m->top.options.gremlin);
     if (level)
6fbf66fa
     {
81d882d5
         struct gc_arena gc = gc_new();
         struct buffer buf = alloc_buf_gc(BUF_SIZE(&m->top.c2.frame), &gc);
         struct packet_flood_parms parm = get_packet_flood_parms(level);
         int i;
6fbf66fa
 
81d882d5
         ASSERT(buf_init(&buf, FRAME_HEADROOM(&m->top.c2.frame)));
         parm.packet_size = min_int(parm.packet_size, MAX_RW_SIZE_TUN(&m->top.c2.frame));
6fbf66fa
 
81d882d5
         msg(D_GREMLIN, "GREMLIN_FLOOD_CLIENTS: flooding clients with %d packets of size %d",
             parm.n_packets,
             parm.packet_size);
6fbf66fa
 
81d882d5
         for (i = 0; i < parm.packet_size; ++i)
4cd4899e
         {
81d882d5
             ASSERT(buf_write_u8(&buf, get_random() & 0xFF));
4cd4899e
         }
6fbf66fa
 
81d882d5
         for (i = 0; i < parm.n_packets; ++i)
4cd4899e
         {
81d882d5
             multi_bcast(m, &buf, NULL, NULL);
4cd4899e
         }
6fbf66fa
 
81d882d5
         gc_free(&gc);
6fbf66fa
     }
 }
81d882d5
 #endif /* ifdef ENABLE_DEBUG */
6fbf66fa
 
72bcdfdc
 static bool
81d882d5
 stale_route_check_trigger(struct multi_context *m)
3a957aae
 {
81d882d5
     struct timeval null;
     CLEAR(null);
     return event_timeout_trigger(&m->stale_routes_check_et, &null, ETT_DEFAULT);
3a957aae
 }
 
6fbf66fa
 /*
  * Process timers in the top-level context
  */
 void
81d882d5
 multi_process_per_second_timers_dowork(struct multi_context *m)
6fbf66fa
 {
81d882d5
     /* possibly reap instances/routes in vhash */
     multi_reap_process(m);
6fbf66fa
 
81d882d5
     /* possibly print to status log */
     if (m->top.c1.status_output)
6fbf66fa
     {
81d882d5
         if (status_trigger(m->top.c1.status_output))
         {
             multi_print_status(m, m->top.c1.status_output, m->status_file_version);
         }
6fbf66fa
     }
 
81d882d5
     /* possibly flush ifconfig-pool file */
     multi_ifconfig_pool_persist(m, false);
6fbf66fa
 
 #ifdef ENABLE_DEBUG
81d882d5
     gremlin_flood_clients(m);
6fbf66fa
 #endif
3a957aae
 
81d882d5
     /* Should we check for stale routes? */
     if (m->top.options.stale_routes_check_interval && stale_route_check_trigger(m))
     {
         check_stale_routes(m);
     }
6fbf66fa
 }
 
 void
81d882d5
 multi_top_init(struct multi_context *m, const struct context *top)
6fbf66fa
 {
81d882d5
     inherit_context_top(&m->top, top);
     m->top.c2.buffers = init_context_buffers(&top->c2.frame);
6fbf66fa
 }
 
 void
81d882d5
 multi_top_free(struct multi_context *m)
6fbf66fa
 {
81d882d5
     close_context(&m->top, -1, CC_GC_FREE);
     free_context_buffers(m->top.c2.buffers);
6fbf66fa
 }
 
8dd9ff8c
 static bool
 is_exit_restart(int sig)
 {
81d882d5
     return (sig == SIGUSR1 || sig == SIGTERM || sig == SIGHUP || sig == SIGINT);
8dd9ff8c
 }
 
 static void
 multi_push_restart_schedule_exit(struct multi_context *m, bool next_server)
 {
81d882d5
     struct hash_iterator hi;
     struct hash_element *he;
     struct timeval tv;
8dd9ff8c
 
81d882d5
     /* tell all clients to restart */
     hash_iterator_init(m->iter, &hi);
     while ((he = hash_iterator_next(&hi)))
8dd9ff8c
     {
81d882d5
         struct multi_instance *mi = (struct multi_instance *) he->value;
         if (!mi->halt)
8dd9ff8c
         {
81d882d5
             send_control_channel_string(&mi->context, next_server ? "RESTART,[N]" : "RESTART", D_PUSH);
             multi_schedule_context_wakeup(m, mi);
8dd9ff8c
         }
     }
81d882d5
     hash_iterator_free(&hi);
8dd9ff8c
 
81d882d5
     /* reschedule signal */
     ASSERT(!openvpn_gettimeofday(&m->deferred_shutdown_signal.wakeup, NULL));
     tv.tv_sec = 2;
     tv.tv_usec = 0;
     tv_add(&m->deferred_shutdown_signal.wakeup, &tv);
8dd9ff8c
 
81d882d5
     m->deferred_shutdown_signal.signal_received = m->top.sig->signal_received;
8dd9ff8c
 
81d882d5
     schedule_add_entry(m->schedule,
                        (struct schedule_entry *) &m->deferred_shutdown_signal,
                        &m->deferred_shutdown_signal.wakeup,
                        compute_wakeup_sigma(&m->deferred_shutdown_signal.wakeup));
8dd9ff8c
 
81d882d5
     m->top.sig->signal_received = 0;
8dd9ff8c
 }
 
6fbf66fa
 /*
  * Return true if event loop should break,
  * false if it should continue.
  */
 bool
81d882d5
 multi_process_signal(struct multi_context *m)
6fbf66fa
 {
81d882d5
     if (m->top.sig->signal_received == SIGUSR2)
6fbf66fa
     {
81d882d5
         struct status_output *so = status_open(NULL, 0, M_INFO, NULL, 0);
         multi_print_status(m, so, m->status_file_version);
         status_close(so);
         m->top.sig->signal_received = 0;
         return false;
6fbf66fa
     }
81d882d5
     else if (proto_is_dgram(m->top.options.ce.proto)
              && is_exit_restart(m->top.sig->signal_received)
              && (m->deferred_shutdown_signal.signal_received == 0)
              && m->top.options.ce.explicit_exit_notification != 0)
8dd9ff8c
     {
81d882d5
         multi_push_restart_schedule_exit(m, m->top.options.ce.explicit_exit_notification == 2);
         return false;
8dd9ff8c
     }
81d882d5
     return true;
6fbf66fa
 }
 
 /*
  * Called when an instance should be closed due to the
  * reception of a soft signal.
  */
 void
81d882d5
 multi_close_instance_on_signal(struct multi_context *m, struct multi_instance *mi)
6fbf66fa
 {
81d882d5
     remap_signal(&mi->context);
     set_prefix(mi);
     print_signal(mi->context.sig, "client-instance", D_MULTI_LOW);
     clear_prefix();
     multi_close_instance(m, mi, false);
6fbf66fa
 }
 
 static void
81d882d5
 multi_signal_instance(struct multi_context *m, struct multi_instance *mi, const int sig)
6fbf66fa
 {
81d882d5
     mi->context.sig->signal_received = sig;
     multi_close_instance_on_signal(m, mi);
6fbf66fa
 }
 
 /*
  * Management subsystem callbacks
  */
 
 #ifdef ENABLE_MANAGEMENT
 
 static void
81d882d5
 management_callback_status(void *arg, const int version, struct status_output *so)
6fbf66fa
 {
81d882d5
     struct multi_context *m = (struct multi_context *) arg;
6fbf66fa
 
81d882d5
     if (!version)
     {
         multi_print_status(m, so, m->status_file_version);
     }
     else
     {
         multi_print_status(m, so, version);
     }
6fbf66fa
 }
 
 static int
81d882d5
 management_callback_n_clients(void *arg)
4fb4ce12
 {
81d882d5
     struct multi_context *m = (struct multi_context *) arg;
     return m->n_clients;
4fb4ce12
 }
 
 static int
81d882d5
 management_callback_kill_by_cn(void *arg, const char *del_cn)
6fbf66fa
 {
81d882d5
     struct multi_context *m = (struct multi_context *) arg;
     struct hash_iterator hi;
     struct hash_element *he;
     int count = 0;
6fbf66fa
 
81d882d5
     hash_iterator_init(m->iter, &hi);
     while ((he = hash_iterator_next(&hi)))
6fbf66fa
     {
81d882d5
         struct multi_instance *mi = (struct multi_instance *) he->value;
         if (!mi->halt)
         {
             const char *cn = tls_common_name(mi->context.c2.tls_multi, false);
             if (cn && !strcmp(cn, del_cn))
             {
                 multi_signal_instance(m, mi, SIGTERM);
                 ++count;
             }
         }
6fbf66fa
     }
81d882d5
     hash_iterator_free(&hi);
     return count;
6fbf66fa
 }
 
 static int
81d882d5
 management_callback_kill_by_addr(void *arg, const in_addr_t addr, const int port)
 {
     struct multi_context *m = (struct multi_context *) arg;
     struct hash_iterator hi;
     struct hash_element *he;
     struct openvpn_sockaddr saddr;
     struct mroute_addr maddr;
     int count = 0;
 
     CLEAR(saddr);
     saddr.addr.in4.sin_family = AF_INET;
     saddr.addr.in4.sin_addr.s_addr = htonl(addr);
     saddr.addr.in4.sin_port = htons(port);
     if (mroute_extract_openvpn_sockaddr(&maddr, &saddr, true))
     {
         hash_iterator_init(m->iter, &hi);
         while ((he = hash_iterator_next(&hi)))
         {
             struct multi_instance *mi = (struct multi_instance *) he->value;
             if (!mi->halt && mroute_addr_equal(&maddr, &mi->real))
             {
                 multi_signal_instance(m, mi, SIGTERM);
                 ++count;
             }
         }
         hash_iterator_free(&hi);
     }
     return count;
6fbf66fa
 }
 
 static void
81d882d5
 management_delete_event(void *arg, event_t event)
6fbf66fa
 {
81d882d5
     struct multi_context *m = (struct multi_context *) arg;
     if (m->mtcp)
     {
         multi_tcp_delete_event(m->mtcp, event);
     }
6fbf66fa
 }
 
81d882d5
 #endif /* ifdef ENABLE_MANAGEMENT */
6fbf66fa
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
 
 static struct multi_instance *
81d882d5
 lookup_by_cid(struct multi_context *m, const unsigned long cid)
90efcacb
 {
81d882d5
     if (m)
90efcacb
     {
81d882d5
         struct multi_instance *mi = (struct multi_instance *) hash_lookup(m->cid_hash, &cid);
         if (mi && !mi->halt)
         {
             return mi;
         }
90efcacb
     }
81d882d5
     return NULL;
90efcacb
 }
 
 static bool
81d882d5
 management_kill_by_cid(void *arg, const unsigned long cid, const char *kill_msg)
90efcacb
 {
81d882d5
     struct multi_context *m = (struct multi_context *) arg;
     struct multi_instance *mi = lookup_by_cid(m, cid);
     if (mi)
90efcacb
     {
81d882d5
         send_restart(&mi->context, kill_msg); /* was: multi_signal_instance (m, mi, SIGTERM); */
         multi_schedule_context_wakeup(m, mi);
         return true;
     }
     else
     {
         return false;
90efcacb
     }
 }
 
 static bool
81d882d5
 management_client_auth(void *arg,
                        const unsigned long cid,
                        const unsigned int mda_key_id,
                        const bool auth,
                        const char *reason,
                        const char *client_reason,
                        struct buffer_list *cc_config)  /* ownership transferred */
 {
     struct multi_context *m = (struct multi_context *) arg;
     struct multi_instance *mi = lookup_by_cid(m, cid);
     bool cc_config_owned = true;
     bool ret = false;
 
     if (mi)
     {
         ret = tls_authenticate_key(mi->context.c2.tls_multi, mda_key_id, auth, client_reason);
         if (ret)
         {
             if (auth)
             {
                 if (!mi->connection_established_flag)
                 {
                     set_cc_config(mi, cc_config);
                     cc_config_owned = false;
                 }
             }
             else
             {
                 if (reason)
                 {
                     msg(D_MULTI_LOW, "MULTI: connection rejected: %s, CLI:%s", reason, np(client_reason));
                 }
                 if (mi->connection_established_flag)
                 {
                     send_auth_failed(&mi->context, client_reason); /* mid-session reauth failed */
                     multi_schedule_context_wakeup(m, mi);
                 }
             }
         }
     }
     if (cc_config_owned && cc_config)
     {
         buffer_list_free(cc_config);
     }
     return ret;
90efcacb
 }
aaf72974
 
 static char *
81d882d5
 management_get_peer_info(void *arg, const unsigned long cid)
aaf72974
 {
81d882d5
     struct multi_context *m = (struct multi_context *) arg;
     struct multi_instance *mi = lookup_by_cid(m, cid);
     char *ret = NULL;
aaf72974
 
81d882d5
     if (mi)
     {
         ret = tls_get_peer_info(mi->context.c2.tls_multi);
     }
aaf72974
 
81d882d5
     return ret;
aaf72974
 }
 
81d882d5
 #endif /* ifdef MANAGEMENT_DEF_AUTH */
90efcacb
 
 #ifdef MANAGEMENT_PF
 static bool
81d882d5
 management_client_pf(void *arg,
                      const unsigned long cid,
                      struct buffer_list *pf_config)  /* ownership transferred */
90efcacb
 {
81d882d5
     struct multi_context *m = (struct multi_context *) arg;
     struct multi_instance *mi = lookup_by_cid(m, cid);
     bool ret = false;
90efcacb
 
81d882d5
     if (mi && pf_config)
     {
         ret = pf_load_from_buffer_list(&mi->context, pf_config);
     }
90efcacb
 
81d882d5
     if (pf_config)
     {
         buffer_list_free(pf_config);
     }
     return ret;
90efcacb
 }
81d882d5
 #endif /* ifdef MANAGEMENT_PF */
90efcacb
 
6fbf66fa
 void
81d882d5
 init_management_callback_multi(struct multi_context *m)
6fbf66fa
 {
 #ifdef ENABLE_MANAGEMENT
81d882d5
     if (management)
     {
         struct management_callback cb;
         CLEAR(cb);
         cb.arg = m;
         cb.flags = MCF_SERVER;
         cb.status = management_callback_status;
         cb.show_net = management_show_net_callback;
         cb.kill_by_cn = management_callback_kill_by_cn;
         cb.kill_by_addr = management_callback_kill_by_addr;
         cb.delete_event = management_delete_event;
         cb.n_clients = management_callback_n_clients;
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
81d882d5
         cb.kill_by_cid = management_kill_by_cid;
         cb.client_auth = management_client_auth;
         cb.get_peer_info = management_get_peer_info;
90efcacb
 #endif
 #ifdef MANAGEMENT_PF
81d882d5
         cb.client_pf = management_client_pf;
90efcacb
 #endif
81d882d5
         management_set_callback(management, &cb);
6fbf66fa
     }
81d882d5
 #endif /* ifdef ENABLE_MANAGEMENT */
6fbf66fa
 }
 
 void
81d882d5
 uninit_management_callback_multi(struct multi_context *m)
6fbf66fa
 {
81d882d5
     uninit_management_callback();
6fbf66fa
 }
 
 /*
  * Top level event loop.
  */
 void
81d882d5
 tunnel_server(struct context *top)
6fbf66fa
 {
81d882d5
     ASSERT(top->options.mode == MODE_SERVER);
6fbf66fa
 
81d882d5
     if (proto_is_dgram(top->options.ce.proto))
     {
         tunnel_server_udp(top);
     }
     else
     {
         tunnel_server_tcp(top);
     }
6fbf66fa
 }
 
81d882d5
 #else  /* if P2MP_SERVER */
 static void
4cd4899e
 dummy(void)
 {
81d882d5
 }
6fbf66fa
 #endif /* P2MP_SERVER */