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.
  *
1c0cc4ad
  *  Copyright (C) 2002-2008 Telethra, 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.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program (see the file COPYING included with this
  *  distribution); if not, write to the Free Software Foundation, Inc.,
  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include "syshead.h"
 
 #if P2MP_SERVER
 
 #include "multi.h"
 #include "push.h"
 #include "misc.h"
 #include "otime.h"
 #include "gremlin.h"
 
 #include "memdbg.h"
 
 #include "forward-inline.h"
90efcacb
 #include "pf-inline.h"
6fbf66fa
 
 /*#define MULTI_DEBUG_EVENT_LOOP*/
 
 #ifdef MULTI_DEBUG_EVENT_LOOP
 static const char *
 id (struct multi_instance *mi)
 {
   if (mi)
     return tls_common_name (mi->context.c2.tls_multi, false);
   else
     return "NULL";
 }
 #endif
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
 static void
 set_cc_config (struct multi_instance *mi, struct buffer_list *cc_config)
 {
   if (mi->cc_config)
     buffer_list_free (mi->cc_config);
   mi->cc_config = cc_config;
 }
 #endif
 
6fbf66fa
 static bool
 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;
3c7f2f55
   struct plugin_list *plugins;
6fbf66fa
 
   /* get environmental variable source */
   if (mi && mi->context.c2.es)
     es = mi->context.c2.es;
   else
     es = env_set_create (&gc);
 
3c7f2f55
   /* get plugin source */
   if (mi)
     plugins = mi->context.plugins;
   else
     plugins = m->top.plugins;
 
   if (plugin_defined (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS))
6fbf66fa
     {
5a2e9a25
       struct argv argv = argv_new ();
       argv_printf (&argv, "%s %s",
 		   op,
 		   mroute_addr_print (addr, &gc));
6fbf66fa
       if (mi)
5a2e9a25
 	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)
6fbf66fa
 	{
 	  msg (M_WARN, "WARNING: learn-address plugin call failed");
 	  ret = false;
 	}
5a2e9a25
       argv_reset (&argv);
6fbf66fa
     }
 
   if (m->top.options.learn_address_script)
     {
5a2e9a25
       struct argv argv = argv_new ();
6fbf66fa
       setenv_str (es, "script_type", "learn-address");
b8fb090c
       argv_printf (&argv, "%sc %s %s",
5a2e9a25
 		   m->top.options.learn_address_script,
 		   op,
 		   mroute_addr_print (addr, &gc));
6fbf66fa
       if (mi)
5a2e9a25
 	argv_printf_cat (&argv, "%s", tls_common_name (mi->context.c2.tls_multi, false));
       if (!openvpn_execve_check (&argv, es, S_SCRIPT, "WARNING: learn-address command failed"))
6fbf66fa
 	ret = false;
5a2e9a25
       argv_reset (&argv);
6fbf66fa
     }
 
   gc_free (&gc);
   return ret;
 }
 
 void
 multi_ifconfig_pool_persist (struct multi_context *m, bool force)
 {
  /* 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)))
     {
       ifconfig_pool_write (m->top.c1.ifconfig_pool_persist, m->ifconfig_pool);
     }
 }
 
 static void
 multi_reap_range (const struct multi_context *m,
 		  int start_bucket,
 		  int end_bucket)
 {
   struct gc_arena gc = gc_new ();
   struct hash_iterator hi;
   struct hash_element *he;
 
   if (start_bucket < 0)
     {
       start_bucket = 0;
       end_bucket = hash_n_buckets (m->vhash);
     }
 
   dmsg (D_MULTI_DEBUG, "MULTI: REAP range %d -> %d", start_bucket, end_bucket);
   hash_iterator_init_range (m->vhash, &hi, true, start_bucket, end_bucket);
   while ((he = hash_iterator_next (&hi)) != NULL)
     {
       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);
 	}
     }
   hash_iterator_free (&hi);
   gc_free (&gc);
 }
 
 static void
 multi_reap_all (const struct multi_context *m)
 {
   multi_reap_range (m, -1, 0);
 }
 
 static struct multi_reap *
 multi_reap_new (int buckets_per_pass)
 {
   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;
 }
 
 void
 multi_reap_process_dowork (const struct multi_context *m)
 {
   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;
 }
 
 static void
 multi_reap_free (struct multi_reap *mr)
 {
   free (mr);
 }
 
 /*
  * How many buckets in vhash to reap per pass.
  */
 static int
 reap_buckets_per_pass (int n_buckets)
 {
   return constrain_int (n_buckets / REAP_DIVISOR, REAP_MIN, REAP_MAX);
 }
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
 
 static uint32_t
 cid_hash_function (const void *key, uint32_t iv)
 {
   const unsigned long *k = (const unsigned long *)key;
   return (uint32_t) *k;
 }
 
 static bool
 cid_compare_function (const void *key1, const void *key2)
 {
   const unsigned long *k1 = (const unsigned long *)key1;
   const unsigned long *k2 = (const unsigned long *)key2;
   return *k1 == *k2;
 }
 
 #endif
 
6fbf66fa
 /*
  * Main initialization function, init multi_context object.
  */
 void
 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,
47ae8457
 		       get_random (),
6fbf66fa
 		       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,
47ae8457
 			get_random (),
6fbf66fa
 			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,
47ae8457
 		       get_random (),
6fbf66fa
 		       mroute_addr_hash_function,
 		       mroute_addr_compare_function);
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
   m->cid_hash = hash_init (t->options.real_hash_size,
 			   0,
 			   cid_hash_function,
 			   cid_compare_function);
 #endif
 
6fbf66fa
   /*
    * 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)
     {
3c7f2f55
       if (dev == DEV_TYPE_TAP)
6fbf66fa
 	{
 	  m->ifconfig_pool = ifconfig_pool_init (IFCONFIG_POOL_INDIV,
 						 t->options.ifconfig_pool_start,
 						 t->options.ifconfig_pool_end,
 						 t->options.duplicate_cn);
 	}
       else if (dev == DEV_TYPE_TUN)
 	{
3c7f2f55
 	  m->ifconfig_pool = ifconfig_pool_init (
 	    (t->options.topology == TOP_NET30) ? IFCONFIG_POOL_30NET : IFCONFIG_POOL_INDIV,
 	    t->options.ifconfig_pool_start,
 	    t->options.ifconfig_pool_end,
 	    t->options.duplicate_cn);
6fbf66fa
 	}
       else
 	{
 	  ASSERT (0);
 	}
 
       /* 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;
 
   /*
    * 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;
 }
 
 const char *
 multi_instance_string (const struct multi_instance *mi, bool null, struct gc_arena *gc)
 {
   if (mi)
     {
       struct buffer out = alloc_buf_gc (256, gc);
       const char *cn = tls_common_name (mi->context.c2.tls_multi, true);
 
       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";
 }
 
 void
 generate_prefix (struct multi_instance *mi)
 {
   mi->msg_prefix = multi_instance_string (mi, true, &mi->gc);
   set_prefix (mi);
 }
 
 void
 ungenerate_prefix (struct multi_instance *mi)
 {
   mi->msg_prefix = NULL;
   set_prefix (mi);
 }
 
90efcacb
 static const char *
 mi_prefix (const struct multi_instance *mi)
 {
   if (mi && mi->msg_prefix)
     return mi->msg_prefix;
   else
     return "UNDEF_I";
 }
 
6fbf66fa
 /*
  * Tell the route helper about deleted iroutes so
  * that it can update its mask of currently used
  * CIDR netlengths.
  */
 static void
 multi_del_iroutes (struct multi_context *m,
 		   struct multi_instance *mi)
 {
   const struct iroute *ir;
   if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN)
     {
       for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
 	mroute_helper_del_iroute (m->route_helper, ir);
     }
 }
 
 static void
 multi_client_disconnect_setenv (struct multi_context *m,
 				struct multi_instance *mi)
 {
   /* setenv client real IP address */
   setenv_trusted (mi->context.c2.es, get_link_socket_info (&mi->context));
 
   /* setenv stats */
e8c1720d
   setenv_counter (mi->context.c2.es, "bytes_received", mi->context.c2.link_read_bytes);
   setenv_counter (mi->context.c2.es, "bytes_sent", mi->context.c2.link_write_bytes);
6fbf66fa
 
a783c400
   /* setenv connection duration */
0b07df0b
   {
     const unsigned int duration = (unsigned int) now - mi->created;
     setenv_unsigned (mi->context.c2.es, "time_duration", duration);
   }
6fbf66fa
 }
 
 static void
 multi_client_disconnect_script (struct multi_context *m,
 				struct multi_instance *mi)
 {
76a59eae
   if ((mi->context.c2.context_auth == CAS_SUCCEEDED && mi->connection_established_flag)
       || mi->context.c2.context_auth == CAS_PARTIAL)
6fbf66fa
     {
       multi_client_disconnect_setenv (m, mi);
 
3c7f2f55
       if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT))
6fbf66fa
 	{
344ee918
 	  if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT, NULL, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
6fbf66fa
 	    msg (M_WARN, "WARNING: client-disconnect plugin call failed");
 	}
 
       if (mi->context.options.client_disconnect_script)
 	{
5a2e9a25
 	  struct argv argv = argv_new ();
6fbf66fa
 	  setenv_str (mi->context.c2.es, "script_type", "client-disconnect");
b8fb090c
 	  argv_printf (&argv, "%sc", mi->context.options.client_disconnect_script);
5a2e9a25
 	  openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-disconnect command failed");
 	  argv_reset (&argv);
6fbf66fa
 	}
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
       if (management)
 	management_notify_client_close (management, &mi->context.c2.mda_context, mi->context.c2.es);
 #endif
 
6fbf66fa
     }
 }
 
 void
 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");
 
   /* 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
       if (mi->did_cid_hash)
 	{
 	  ASSERT (hash_remove (m->cid_hash, &mi->context.c2.mda_context.cid));
 	}
 #endif
6fbf66fa
 
       schedule_remove_entry (m->schedule, (struct schedule_entry *) mi);
 
       ifconfig_pool_release (m->ifconfig_pool, mi->vaddr_handle, false);
       
       if (mi->did_iroutes)
         {
           multi_del_iroutes (m, mi);
           mi->did_iroutes = false;
         }
 
       if (m->mtcp)
 	multi_tcp_dereference_instance (m->mtcp, mi);
 
       mbuf_dereference_instance (m->mbuf, mi);
     }
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
   set_cc_config (mi, NULL);
 #endif
 
6fbf66fa
   multi_client_disconnect_script (m, mi);
 
   if (mi->did_open_context)
     close_context (&mi->context, SIGTERM, CC_GC_FREE);
 
   multi_tcp_instance_specific_free (mi);
 
   ungenerate_prefix (mi);
 
   /*
    * 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);
 
   perf_pop ();
 }
 
 /*
  * Called on shutdown or restart.
  */
 void
 multi_uninit (struct multi_context *m)
 {
   if (m->thread_mode & MC_WORK_THREAD)
     {
       multi_top_free (m);
       m->thread_mode = MC_UNDEF;
     }
   else if (m->thread_mode)
     {
       if (m->hash)
 	{
 	  struct hash_iterator hi;
 	  struct hash_element *he;
 
 	  hash_iterator_init (m->iter, &hi, true);
 	  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
 	  hash_free (m->cid_hash);
 #endif
6fbf66fa
 	  m->hash = NULL;
 
 	  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;
 	}
     }
 }
 
 /*
  * Create a client instance object for a newly connected client.
  */
 struct multi_instance *
 multi_create_instance (struct multi_context *m, const struct mroute_addr *real)
 {
   struct gc_arena gc = gc_new ();
   struct multi_instance *mi;
 
   perf_push (PERF_MULTI_CREATE_INSTANCE);
 
   msg (D_MULTI_LOW, "MULTI: multi_create_instance called");
 
   ALLOC_OBJ_CLEAR (mi, struct multi_instance);
 
   mutex_init (&mi->mutex);
   mi->gc = gc_new ();
   multi_instance_inc_refcount (mi);
   mi->vaddr_handle = -1;
   mi->created = now;
   mroute_addr_init (&mi->real);
 
   if (real)
     {
       mi->real = *real;
       generate_prefix (mi);
     }
 
79df31c8
   mi->did_open_context = true;
6fbf66fa
   inherit_context_child (&mi->context, &m->top);
   if (IS_SIG (&mi->context))
     goto err;
 
   mi->context.c2.context_auth = CAS_PENDING;
 
   if (hash_n_elements (m->hash) >= m->max_clients)
     {
       msg (D_MULTI_ERRORS, "MULTI: new incoming connection would exceed maximum number of clients (%d)", m->max_clients);
       goto err;
     }
 
   if (!real) /* TCP mode? */
     {
       if (!multi_tcp_instance_specific_init (m, mi))
 	goto err;
       generate_prefix (mi);
     }
 
   if (!hash_add (m->iter, &mi->real, mi, false))
     {
       msg (D_MULTI_LOW, "MULTI: unable to add real address [%s] to iterator hash table",
 	   mroute_addr_print (&mi->real, &gc));
       goto err;
     }
   mi->did_iter = true;
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
   do {
     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;
 #endif
 
6fbf66fa
   mi->context.c2.push_reply_deferred = true;
 
   if (!multi_process_post (m, mi, MPP_PRE_SELECT))
     {
       msg (D_MULTI_ERRORS, "MULTI: signal occurred during client instance initialization");
       goto err;
     }
 
   perf_pop ();
   gc_free (&gc);
   return mi;
 
  err:
   multi_close_instance (m, mi, false);
   perf_pop ();
   gc_free (&gc);
   return NULL;
 }
 
 /*
  * Dump tables -- triggered by SIGUSR2.
  * If status file is defined, write to file.
  * If status file is NULL, write to syslog.
  */
 void
 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, PACKAGE_NAME " 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, true);
 	  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, true);
 	  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)
 	{
 	  /*
 	   * Status file version 2
 	   */
 	  status_printf (so, "TITLE,%s", title_string);
 	  status_printf (so, "TIME,%s,%u", time_string (now, 0, false, &gc_top), (unsigned int)now);
 	  status_printf (so, "HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t)");
 	  hash_iterator_init (m->hash, &hi, true);
 	  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,%s,%s,%s," counter_format "," counter_format ",%s,%u",
 				 tls_common_name (mi->context.c2.tls_multi, false),
 				 mroute_addr_print (&mi->real, &gc),
 				 print_in_addr_t (mi->reporting_addr, IA_EMPTY_IF_UNDEF, &gc),
 				 mi->context.c2.link_read_bytes,
 				 mi->context.c2.link_write_bytes,
 				 time_string (mi->created, 0, false, &gc),
 				 (unsigned int)mi->created);
 		}
 	      gc_free (&gc);
 	    }
 	  hash_iterator_free (&hi);
 
 	  status_printf (so, "HEADER,ROUTING_TABLE,Virtual Address,Common Name,Real Address,Last Ref,Last Ref (time_t)");
 	  hash_iterator_init (m->vhash, &hi, true);
 	  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,%s%s,%s,%s,%s,%u",
 				 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),
 				 (unsigned int)route->last_reference);
 		}
 	      gc_free (&gc);
 	    }
 	  hash_iterator_free (&hi);
 
 	  if (m->mbuf)
 	    status_printf (so, "GLOBAL_STATS,Max bcast/mcast queue length,%d",
 			   mbuf_maximum_queued (m->mbuf));
 
 	  status_printf (so, "END");
 	}
       else
 	{
 	  status_printf (so, "ERROR: bad status format version number");
 	}
3c7f2f55
 
 #ifdef PACKET_TRUNCATION_CHECK
       {
 	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, true);
 	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
 
6fbf66fa
       status_flush (so);
       gc_free (&gc_top);
     }
 }
 
 /*
  * 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 *
 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;
 
   hash_bucket_lock (bucket);
 
   /* 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? */
   if ((!owner || owner != mi)
       && mroute_learnable_address (addr)
       && !mroute_addr_equal (addr, &m->local))
     {
       struct gc_arena gc = gc_new ();
       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);
 
       gc_free (&gc);
     }
 
   hash_bucket_unlock (bucket);
   return owner;
 }
 
 /*
  * Get client instance based on virtual address.
  */
 static struct multi_instance *
 multi_get_instance_by_virtual_addr (struct multi_context *m,
 				    const struct mroute_addr *addr,
 				    bool cidr_routing)
 {
   struct multi_route *route;
   struct multi_instance *ret = NULL;
 
   /* check for local address */
   if (mroute_addr_equal (addr, &m->local))
     return NULL;
 
   route = (struct multi_route *) hash_lookup (m->vhash, addr);
 
   /* does host route (possible cached) exist? */
   if (route && multi_route_defined (m, route))
     {
       struct multi_instance *mi = route->instance;
       route->last_reference = now;
       ret = mi;
     }
   else if (cidr_routing) /* do we need to regenerate a host route cache entry? */
     {
       struct mroute_helper *rh = m->route_helper;
       struct mroute_addr tryaddr;
       int i;
 
       mroute_helper_lock (rh);
 
       /* 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;
 	    }
 	}
       
       mroute_helper_unlock (rh);
     }
   
 #ifdef ENABLE_DEBUG
   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);
     }
 #endif
 
   ASSERT (!(ret && ret->halt));
   return ret;
 }
 
 /*
  * Helper function to multi_learn_addr().
  */
 static struct multi_instance *
 multi_learn_in_addr_t (struct multi_context *m,
 		       struct multi_instance *mi,
 		       in_addr_t a,
90efcacb
 		       int netbits, /* -1 if host route, otherwise # of network bits in address */
 		       bool primary)
6fbf66fa
 {
8bc93d7f
   struct openvpn_sockaddr remote_si;
6fbf66fa
   struct mroute_addr addr;
 
   CLEAR (remote_si);
8bc93d7f
   remote_si.sa.sin_family = AF_INET;
   remote_si.sa.sin_addr.s_addr = htonl (a);
   ASSERT (mroute_extract_openvpn_sockaddr (&addr, &remote_si, false));
6fbf66fa
 
   if (netbits >= 0)
     {
       addr.type |= MR_WITH_NETBITS;
       addr.netbits = (uint8_t) netbits;
     }
90efcacb
 
   {
     struct multi_instance *owner = multi_learn_addr (m, mi, &addr, 0);
 #ifdef MANAGEMENT_DEF_AUTH
     if (management && owner)
       management_learn_addr (management, &mi->context.c2.mda_context, &addr, primary);
 #endif
     return owner;
   }
6fbf66fa
 }
 
 /*
  * A new client has connected, add routes (server -> client)
  * to internal routing table.
  */
 static void
 multi_add_iroutes (struct multi_context *m,
 		   struct multi_instance *mi)
 {
   struct gc_arena gc = gc_new ();
   const struct iroute *ir;
   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_iroute (m->route_helper, ir);
       
90efcacb
 	  multi_learn_in_addr_t (m, mi, ir->network, ir->netbits, false);
6fbf66fa
 	}
     }
   gc_free (&gc);
 }
 
 /*
  * Given an instance (new_mi), delete all other instances which use the
  * same common name.
  */
 static void
 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;
7b0a8146
 	  int count = 0;
6fbf66fa
 
 	  hash_iterator_init (m->iter, &hi, true);
 	  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);
7b0a8146
 		      ++count;
6fbf66fa
 		    }
 		}
 	    }
 	  hash_iterator_free (&hi);
7b0a8146
 
 	  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
 	}
     }
 }
 
 /*
3c7f2f55
  * Ensure that endpoint to be pushed to client
  * complies with --ifconfig-push-constraint directive.
  */
 static bool
 ifconfig_push_constraint_satisfied (const struct context *c)
 {
   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;
 }
 
 /*
6fbf66fa
  * Select a virtual address for a new client instance.
  * Use an --ifconfig-push directive, if given (static IP).
  * Otherwise use an --ifconfig-pool address (dynamic IP). 
  */
 static void
 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;
     }
   else if (m->ifconfig_pool && mi->vaddr_handle < 0) /* otherwise, choose a pool address */
     {
       in_addr_t local=0, remote=0;
       const char *cn = NULL;
 
       if (!mi->context.options.duplicate_cn)
 	cn = tls_common_name (mi->context.c2.tls_multi, true);
 
       mi->vaddr_handle = ifconfig_pool_acquire (m->ifconfig_pool, &local, &remote, cn);
       if (mi->vaddr_handle >= 0)
 	{
3c7f2f55
 	  const int tunnel_type = TUNNEL_TYPE (mi->context.c1.tuntap);
 	  const int tunnel_topology = TUNNEL_TOPOLOGY (mi->context.c1.tuntap);
 
 	  /* set push_ifconfig_remote_netmask from pool ifconfig address(es) */
6fbf66fa
 	  mi->context.c2.push_ifconfig_local = remote;
3c7f2f55
 	  if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET))
6fbf66fa
 	    {
 	      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;
 	    }
3c7f2f55
 	  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));
6fbf66fa
 	}
       else
 	{
 	  msg (D_MULTI_ERRORS, "MULTI: no free --ifconfig-pool addresses are available");
 	}
     }
   gc_free (&gc);
 }
 
 /*
  * Set virtual address environmental variables.
  */
 static void
 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)
     {
3c7f2f55
       const int tunnel_type = TUNNEL_TYPE (mi->context.c1.tuntap);
       const int tunnel_topology = TUNNEL_TOPOLOGY (mi->context.c1.tuntap);
 
6fbf66fa
       setenv_in_addr_t (mi->context.c2.es,
 			"ifconfig_pool_remote_ip",
 			mi->context.c2.push_ifconfig_local,
 			SA_SET_IF_NONZERO);
 
3c7f2f55
       if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET))
6fbf66fa
 	{
 	  setenv_in_addr_t (mi->context.c2.es,
3c7f2f55
 			    "ifconfig_pool_netmask",
6fbf66fa
 			    mi->context.c2.push_ifconfig_remote_netmask,
 			    SA_SET_IF_NONZERO);
 	}
3c7f2f55
       else if (tunnel_type == DEV_TYPE_TUN)
6fbf66fa
 	{
 	  setenv_in_addr_t (mi->context.c2.es,
3c7f2f55
 			    "ifconfig_pool_local_ip",
6fbf66fa
 			    mi->context.c2.push_ifconfig_remote_netmask,
 			    SA_SET_IF_NONZERO);
 	}
     }
 }
 
 /*
3c7f2f55
  * Called after client-connect script is called
6fbf66fa
  */
 static void
 multi_client_connect_post (struct multi_context *m,
 			   struct multi_instance *mi,
 			   const char *dc_file,
 			   unsigned int option_permissions_mask,
 			   unsigned int *option_types_found)
 {
   /* Did script generate a dynamic config file? */
   if (test_file (dc_file))
     {
       options_server_import (&mi->context.options,
 			     dc_file,
 			     D_IMPORT_ERRORS|M_OPTERR,
 			     option_permissions_mask,
 			     option_types_found,
 			     mi->context.c2.es);
 
       if (!delete_file (dc_file))
 	msg (D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s",
 	     dc_file);
 
       /*
        * 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
 #ifdef ENABLE_PLUGIN
 
 /*
  * Called after client-connect plug-in is called
  */
 static void
 multi_client_connect_post_plugin (struct multi_context *m,
 				  struct multi_instance *mi,
 				  const struct plugin_return *pr,
 				  unsigned int option_permissions_mask,
 				  unsigned int *option_types_found)
 {
   struct plugin_return config;
 
   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)
90efcacb
 	    options_string_import (&mi->context.options,
3c7f2f55
 				   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);
     }
 }
 
 #endif
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
 
 /*
  * Called to load management-derived client-connect config
  */
 static void
 multi_client_connect_mda (struct multi_context *m,
 			  struct multi_instance *mi,
 			  const struct buffer_list *config,
 			  unsigned int option_permissions_mask,
 			  unsigned int *option_types_found)
 {
   if (config)
     {
       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);
     }
 }
 
 #endif
 
a783c400
 static void
 multi_client_connect_setenv (struct multi_context *m,
 			     struct multi_instance *mi)
 {
   struct gc_arena gc = gc_new ();
 
   /* setenv incoming cert common name for script */
   setenv_str (mi->context.c2.es, "common_name", tls_common_name (mi->context.c2.tls_multi, true));
 
   /* setenv client real IP address */
   setenv_trusted (mi->context.c2.es, get_link_socket_info (&mi->context));
 
   /* setenv client virtual IP address */
   multi_set_virtual_addr_env (m, mi);
 
   /* setenv connection time */
   {
     const char *created_ascii = time_string (mi->created, 0, false, &gc);
     setenv_str (mi->context.c2.es, "time_ascii", created_ascii);
     setenv_unsigned (mi->context.c2.es, "time_unix", (unsigned int)mi->created);
   }
 
   gc_free (&gc);
 }
 
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
 multi_connection_established (struct multi_context *m, struct multi_instance *mi)
 {
344ee918
   if (tls_authentication_status (mi->context.c2.tls_multi, 0) == TLS_AUTHENTICATION_SUCCEEDED)
6fbf66fa
     {
       struct gc_arena gc = gc_new ();
       unsigned int option_types_found = 0;
c9ee6dee
 
537073fd
       const unsigned int option_permissions_mask =
c9ee6dee
 	  OPT_P_INSTANCE
 	| OPT_P_INHERIT
 	| OPT_P_PUSH
 	| OPT_P_TIMER
 	| OPT_P_CONFIG
 	| OPT_P_ECHO
 	| OPT_P_COMP
 	| OPT_P_SOCKFLAGS;
 
6fbf66fa
       int cc_succeeded = true; /* client connect script status */
       int cc_succeeded_count = 0;
 
       ASSERT (mi->context.c1.tuntap);
 
       /* lock down the common name so it can't change during future TLS renegotiations */
       tls_lock_common_name (mi->context.c2.tls_multi);
 
       /* generate a msg() prefix for this client instance */
       generate_prefix (mi);
 
       /* 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;
 	  
 	  ccd_file = gen_path (mi->context.options.client_config_dir,
 			       tls_common_name (mi->context.c2.tls_multi, false),
 			       &gc);
 
 	  /* try common-name file */
 	  if (test_file (ccd_file))
 	    {
 	      options_server_import (&mi->context.options,
 				     ccd_file,
 				     D_IMPORT_ERRORS|M_OPTERR,
 				     option_permissions_mask,
 				     &option_types_found,
 				     mi->context.c2.es);
 	    }
 	  else /* try default file */
 	    {
 	      ccd_file = gen_path (mi->context.options.client_config_dir,
 				   CCD_DEFAULT,
 				   &gc);
 
 	      if (test_file (ccd_file))
 		{
 		  options_server_import (&mi->context.options,
 					 ccd_file,
 					 D_IMPORT_ERRORS|M_OPTERR,
 					 option_permissions_mask,
 					 &option_types_found,
 					 mi->context.c2.es);
 		}
 	    }
 	}
 
       /*
        * Select a virtual address from either --ifconfig-push in --client-config-dir file
        * or --ifconfig-pool.
        */
       multi_select_virtual_addr (m, mi);
 
a783c400
       /* do --client-connect setenvs */
       multi_client_connect_setenv (m, mi);
6fbf66fa
 
3c7f2f55
 #ifdef ENABLE_PLUGIN
6fbf66fa
       /*
        * Call client-connect plug-in.
        */
3c7f2f55
 
       /* deprecated callback, use a file for passing back return info */
       if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT))
6fbf66fa
 	{
5a2e9a25
 	  struct argv argv = argv_new ();
344ee918
 	  const char *dc_file = create_temp_filename (mi->context.options.tmp_dir, "cc", &gc);
5a2e9a25
 	  argv_printf (&argv, "%s", dc_file);
6fbf66fa
 	  delete_file (dc_file);
5a2e9a25
 	  if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, &argv, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
6fbf66fa
 	    {
 	      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;
 	    }
5a2e9a25
 	  argv_reset (&argv);
6fbf66fa
 	}
 
3c7f2f55
       /* 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);
 
344ee918
 	  if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2, NULL, &pr, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
3c7f2f55
 	    {
 	      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
 
6fbf66fa
       /*
        * Run --client-connect script.
        */
       if (mi->context.options.client_connect_script && cc_succeeded)
 	{
5a2e9a25
 	  struct argv argv = argv_new ();
6fbf66fa
 	  const char *dc_file = NULL;
 
 	  setenv_str (mi->context.c2.es, "script_type", "client-connect");
 
344ee918
 	  dc_file = create_temp_filename (mi->context.options.tmp_dir, "cc", &gc);
6fbf66fa
 
 	  delete_file (dc_file);
 
b8fb090c
 	  argv_printf (&argv, "%sc %s",
5a2e9a25
 		       mi->context.options.client_connect_script,
 		       dc_file);
6fbf66fa
 
5a2e9a25
 	  if (openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-connect command failed"))
6fbf66fa
 	    {
 	      multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found);
 	      ++cc_succeeded_count;
 	    }
 	  else
 	    cc_succeeded = false;
5a2e9a25
 
 	  argv_reset (&argv);
6fbf66fa
 	}
 
       /*
90efcacb
        * Check for client-connect script left by management interface client
        */
 #ifdef MANAGEMENT_DEF_AUTH
       if (cc_succeeded && mi->cc_config)
 	{
 	  multi_client_connect_mda (m, mi, mi->cc_config, option_permissions_mask, &option_types_found);
 	  ++cc_succeeded_count;
 	}
 #endif
 
       /*
6fbf66fa
        * 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;
 	}
 
       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));
 	    }
 
 	  /*
3c7f2f55
 	   * 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));
 	    }
 
 	  /*
6fbf66fa
 	   * 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)
 		{
90efcacb
 		  multi_learn_in_addr_t (m, mi, mi->context.c2.push_ifconfig_local, -1, true);
6fbf66fa
 		  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));
 		}
 
 	      /* 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;
 
 	  /* set context-level authentication flag */
 	  mi->context.c2.context_auth = CAS_SUCCEEDED;
 	}
       else
 	{
 	  /* set context-level authentication flag */
 	  mi->context.c2.context_auth = cc_succeeded_count ? CAS_PARTIAL : CAS_FAILED;
 	}
 
       /* set flag so we don't get called again */
       mi->connection_established_flag = true;
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
       if (management)
 	management_connection_established (management, &mi->context.c2.mda_context);
 #endif
 
6fbf66fa
       gc_free (&gc);
     }
 
   /*
    * Reply now to client's PUSH_REQUEST query
    */
   mi->context.c2.push_reply_deferred = false;
 }
 
 /*
  * Add a mbuf buffer to a particular
  * instance.
  */
 void
 multi_add_mbuf (struct multi_context *m,
 		struct multi_instance *mi,
 		struct mbuf_buffer *mb)
 {
   if (multi_output_queue_ready (m, mi))
     {
       struct mbuf_item item;
       item.buffer = mb;
       item.instance = mi;
       mbuf_add_item (m->mbuf, &item);
     }
   else
     {
       msg (D_MULTI_DROPPED, "MULTI: packet dropped due to output saturation (multi_add_mbuf)");
     }
 }
 
 /*
  * Add a packet to a client instance output queue.
  */
 static inline void
 multi_unicast (struct multi_context *m,
 	       const struct buffer *buf,
 	       struct multi_instance *mi)
 {
   struct mbuf_buffer *mb;
 
   if (BLEN (buf) > 0)
     {
       mb = mbuf_alloc_buf (buf);
       mb->flags = MF_UNICAST;
       multi_add_mbuf (m, mi, mb);
       mbuf_free_buf (mb);
     }
 }
 
 /*
  * Broadcast a packet to all clients.
  */
90efcacb
 static void
6fbf66fa
 multi_bcast (struct multi_context *m,
 	     const struct buffer *buf,
90efcacb
 	     const struct multi_instance *sender_instance,
 	     const struct mroute_addr *sender_addr)
6fbf66fa
 {
   struct hash_iterator hi;
   struct hash_element *he;
   struct multi_instance *mi;
   struct mbuf_buffer *mb;
 
   if (BLEN (buf) > 0)
     {
       perf_push (PERF_MULTI_BCAST);
 #ifdef MULTI_DEBUG_EVENT_LOOP
       printf ("BCAST len=%d\n", BLEN (buf));
 #endif
       mb = mbuf_alloc_buf (buf);
       hash_iterator_init (m->iter, &hi, true);
 
       while ((he = hash_iterator_next (&hi)))
 	{
 	  mi = (struct multi_instance *) he->value;
90efcacb
 	  if (mi != sender_instance && !mi->halt)
 	    {
 #ifdef ENABLE_PF
 	      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
 	      multi_add_mbuf (m, mi, mb);
 	    }
6fbf66fa
 	}
 
       hash_iterator_free (&hi);
       mbuf_free_buf (mb);
       perf_pop ();
     }
 }
 
 /*
  * 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
 compute_wakeup_sigma (const struct timeval *delta)
 {
   if (delta->tv_sec < 1)
     {
       /* if < 1 sec, fuzz = # of microseconds / 8 */
       return delta->tv_usec >> 3;
     }
   else
     {
       /* 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 */
     }
 }
 
 /*
  * 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
 multi_process_post (struct multi_context *m, struct multi_instance *mi, const unsigned int flags)
 {
   bool ret = true;
 
   if (!IS_SIG (&mi->context) && ((flags & MPP_PRE_SELECT) || ((flags & MPP_CONDITIONAL_PRE_SELECT) && !ANY_OUT (&mi->context))))
     {
       /* figure timeouts and fetch possible outgoing
 	 to_link packets (such as ping or TLS control) */
       pre_select (&mi->context);
 
       if (!IS_SIG (&mi->context))
 	{
 	  /* calculate an absolute wakeup time */
0475d17e
 	  ASSERT (!openvpn_gettimeofday (&mi->wakeup, NULL));
6fbf66fa
 	  tv_add (&mi->wakeup, &mi->context.c2.timeval);
 
 	  /* 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));
 
 	  /* 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);
 	}
     }
 
   if (IS_SIG (&mi->context))
     {
       if (flags & MPP_CLOSE_ON_SIGNAL)
 	{
 	  multi_close_instance_on_signal (m, mi);
 	  ret = false;
 	}
     }
   else
     {
       /* continue to pend on output? */
       multi_set_pending (m, ANY_OUT (&mi->context) ? mi : NULL);
 
 #ifdef MULTI_DEBUG_EVENT_LOOP
       printf ("POST %s[%d] to=%d lo=%d/%d w=%d/%d\n",
 	      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,
 	      (int)mi->context.c2.timeval.tv_sec,
 	      (int)mi->context.c2.timeval.tv_usec);
 #endif
     }
 
   if ((flags & MPP_RECORD_TOUCH) && m->mpp_touched)
     *m->mpp_touched = mi;
 
   return ret;
 }
 
 /*
  * Process packets in the TCP/UDP socket -> TUN/TAP interface direction,
  * i.e. client -> server direction.
  */
 bool
 multi_process_incoming_link (struct multi_context *m, struct multi_instance *instance, const unsigned int mpp_flags)
 {
   struct gc_arena gc = gc_new ();
 
   struct context *c;
   struct mroute_addr src, dest;
   unsigned int mroute_flags;
   struct multi_instance *mi;
   bool ret = true;
 
ba3ff466
   if (m->pending)
     return true;
6fbf66fa
 
   if (!instance)
     {
 #ifdef MULTI_DEBUG_EVENT_LOOP
       printf ("TCP/UDP -> TUN [%d]\n", BLEN (&m->top.c2.buf));
 #endif
       multi_set_pending (m, multi_get_create_instance_udp (m));
     }
   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 */
 	  c->c2.from = m->top.c2.from;
 	}
 
       if (BLEN (&c->c2.buf) > 0)
 	{
 	  /* decrypt in instance context */
 	  process_incoming_link (c);
 
 	  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,
90efcacb
 							      NULL,
 							      NULL,
6fbf66fa
 							      &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)
 		{
 		  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 */
90efcacb
 		      multi_bcast (m, &c->c2.to_tun, m->pending, NULL);
6fbf66fa
 		    }
 		  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
90efcacb
 			  if (!pf_c2c_test (c, &mi->context, "tun_c2c"))
47ae8457
 			    {
90efcacb
 			      msg (D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TUN packet filter",
 				   mi_prefix (mi));
47ae8457
 			    }
 			  else
 #endif
 			    {
 			      multi_unicast (m, &c->c2.to_tun, mi);
 			      register_activity (c, BLEN(&c->c2.to_tun));
 			    }
6fbf66fa
 			  c->c2.to_tun.len = 0;
 			}
 		    }
 		}
47ae8457
 #ifdef ENABLE_PF
90efcacb
 	      if (c->c2.to_tun.len && !pf_addr_test (c, &dest, "tun_dest_addr"))
47ae8457
 		{
90efcacb
 		  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
6fbf66fa
 	    }
 	  else if (TUNNEL_TYPE (m->top.c1.tuntap) == DEV_TYPE_TAP)
 	    {
90efcacb
 #ifdef ENABLE_PF
 	      struct mroute_addr edest;
 	      mroute_addr_reset (&edest);
 #endif
6fbf66fa
 	      /* extract packet source and dest addresses */
 	      mroute_flags = mroute_extract_addr_from_packet (&src,
 							      &dest,
90efcacb
 							      NULL,
 #ifdef ENABLE_PF
 							      &edest,
 #else
 							      NULL,
 #endif
6fbf66fa
 							      &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))
 			    {
90efcacb
 			      multi_bcast (m, &c->c2.to_tun, m->pending, NULL);
6fbf66fa
 			    }
 			  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
 				  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
 #endif
 				    {
 				      multi_unicast (m, &c->c2.to_tun, mi);
 				      register_activity (c, BLEN(&c->c2.to_tun));
 				    }
6fbf66fa
 				  c->c2.to_tun.len = 0;
 				}
 			    }
 			}
90efcacb
 #ifdef ENABLE_PF
 		      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;
 			}
 #endif
6fbf66fa
 		    }
 		  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;
 		}
 	    }
 	}
 
       /* postprocess and set wakeup */
       ret = multi_process_post (m, m->pending, mpp_flags);
 
       clear_prefix ();
     }
 
   gc_free (&gc);
   return ret;
 }
 
 /*
  * Process packets in the TUN/TAP interface -> TCP/UDP socket direction,
  * i.e. server -> client direction.
  */
 bool
 multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flags)
 {
   struct gc_arena gc = gc_new ();
   bool ret = true;
 
   if (BLEN (&m->top.c2.buf) > 0)
     {
       unsigned int mroute_flags;
       struct mroute_addr src, dest;
       const int dev_type = TUNNEL_TYPE (m->top.c1.tuntap);
 
90efcacb
 #ifdef ENABLE_PF
       struct mroute_addr esrc, *e1, *e2;
       if (dev_type == DEV_TYPE_TUN)
 	{
 	  e1 = NULL;
 	  e2 = &src;
 	}
       else
 	{
 	  e1 = e2 = &esrc;
 	  mroute_addr_reset (&esrc);
 	}
 #endif
 
6fbf66fa
 #ifdef MULTI_DEBUG_EVENT_LOOP
       printf ("TUN -> TCP/UDP [%d]\n", BLEN (&m->top.c2.buf));
 #endif
 
ba3ff466
       if (m->pending)
 	return true;
6fbf66fa
 
       /* 
        * Route an incoming tun/tap packet to
        * the appropriate multi_instance object.
        */
 
       mroute_flags = mroute_extract_addr_from_packet (&src,
 						      &dest,
90efcacb
 #ifdef ENABLE_PF
 						      e1,
 #else
 						      NULL,
 #endif
 						      NULL,
6fbf66fa
 						      &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
 	      multi_bcast (m, &m->top.c2.buf, NULL, e2);
 #else
 	      multi_bcast (m, &m->top.c2.buf, NULL, NULL);
 #endif
6fbf66fa
 	    }
 	  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);
 
47ae8457
 #ifdef ENABLE_PF
90efcacb
 		  if (!pf_addr_test (c, e2, "tun_tap_src_addr"))
6fbf66fa
 		    {
90efcacb
 		      msg (D_PF_DROPPED, "PF: addr[%s] -> client packet dropped by packet filter",
 			   mroute_addr_print_ex (&src, MAPF_SHOW_ARP, &gc));
47ae8457
 		      buf_reset_len (&c->c2.buf);
6fbf66fa
 		    }
 		  else
47ae8457
 #endif
 		  {
 		    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);
 		      }
 		  }
6fbf66fa
 	      
 		  /* 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;
 }
 
 /*
  * Process a possible client-to-client/bcast/mcast message in the
  * queue.
  */
 struct multi_instance *
 multi_get_queue (struct mbuf_set *ms)
 {
   struct mbuf_item item;
 
   if (mbuf_extract_item (ms, &item, true)) /* cleartext IP packet */
     {
       unsigned int pipv4_flags = PIPV4_PASSTOS;
 
       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 */
 	pipv4_flags |= PIPV4_MSSFIX;
       process_ipv4_header (&item.instance->context, pipv4_flags, &item.instance->context.c2.buf);
       encrypt_sign (&item.instance->context, true);
       mbuf_free_buf (item.buffer);
 
       dmsg (D_MULTI_DEBUG, "MULTI: C2C/MCAST/BCAST");
 
       clear_prefix ();
       return item.instance;
     }
   else
     {
       return NULL;
     }
 }
 
 /*
  * Called when an I/O wait times out.  Usually means that a particular
  * client instance object needs timer-based service.
  */
 bool
 multi_process_timeout (struct multi_context *m, const unsigned int mpp_flags)
 {
   bool ret = true;
 
 #ifdef MULTI_DEBUG_EVENT_LOOP
   printf ("%s -> TIMEOUT\n", id(m->earliest_wakeup));
 #endif
 
   /* instance marked for wakeup? */
   if (m->earliest_wakeup)
     {
       set_prefix (m->earliest_wakeup);
       ret = multi_process_post (m, m->earliest_wakeup, mpp_flags);
       m->earliest_wakeup = NULL;
       clear_prefix ();
     }
   return ret;
 }
 
 /*
  * Drop a TUN/TAP outgoing packet..
  */
 void
 multi_process_drop_outgoing_tun (struct multi_context *m, const unsigned int mpp_flags)
 {
   struct multi_instance *mi = m->pending;
 
   ASSERT (mi);
 
   set_prefix (mi);
 
   msg (D_MULTI_ERRORS, "MULTI: Outgoing TUN queue full, dropped packet len=%d",
        mi->context.c2.to_tun.len);
 
   buf_reset (&mi->context.c2.to_tun);
 
   multi_process_post (m, mi, mpp_flags);
   clear_prefix ();
 }
 
 /*
  * Per-client route quota management
  */
 
 void
 route_quota_exceeded (const struct multi_context *m, const struct multi_instance *mi)
 {
   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);
 }
 
 #ifdef ENABLE_DEBUG
 /*
  * Flood clients with random packets
  */
 static void
 gremlin_flood_clients (struct multi_context *m)
 {
   const int level = GREMLIN_PACKET_FLOOD_LEVEL (m->top.options.gremlin);
   if (level)
     {
       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;
 
       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));
 
       msg (D_GREMLIN, "GREMLIN_FLOOD_CLIENTS: flooding clients with %d packets of size %d",
 	   parm.n_packets,
 	   parm.packet_size);
 
       for (i = 0; i < parm.packet_size; ++i)
 	ASSERT (buf_write_u8 (&buf, get_random () & 0xFF));
 
       for (i = 0; i < parm.n_packets; ++i)
90efcacb
 	multi_bcast (m, &buf, NULL, NULL);
6fbf66fa
 
       gc_free (&gc);
     }
 }
 #endif
 
 /*
  * Process timers in the top-level context
  */
 void
 multi_process_per_second_timers_dowork (struct multi_context *m)
 {
   /* possibly reap instances/routes in vhash */
   multi_reap_process (m);
 
   /* possibly print to status log */
   if (m->top.c1.status_output)
     {
       if (status_trigger (m->top.c1.status_output))
 	multi_print_status (m, m->top.c1.status_output, m->status_file_version);
     }
 
   /* possibly flush ifconfig-pool file */
   multi_ifconfig_pool_persist (m, false);
 
 #ifdef ENABLE_DEBUG
   gremlin_flood_clients (m);
 #endif
 }
 
 void
 multi_top_init (struct multi_context *m, const struct context *top, const bool alloc_buffers)
 {
   inherit_context_top (&m->top, top);
   m->top.c2.buffers = NULL;
   if (alloc_buffers)
     m->top.c2.buffers = init_context_buffers (&top->c2.frame);
 }
 
 void
 multi_top_free (struct multi_context *m)
 {
   close_context (&m->top, -1, CC_GC_FREE);
   free_context_buffers (m->top.c2.buffers);
 }
 
 /*
  * Return true if event loop should break,
  * false if it should continue.
  */
 bool
 multi_process_signal (struct multi_context *m)
 {
   if (m->top.sig->signal_received == SIGUSR2)
     {
       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;
     }
   return true;
 }
 
 /*
  * Called when an instance should be closed due to the
  * reception of a soft signal.
  */
 void
 multi_close_instance_on_signal (struct multi_context *m, struct multi_instance *mi)
 {
   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);
 }
 
 static void
 multi_signal_instance (struct multi_context *m, struct multi_instance *mi, const int sig)
 {
   mi->context.sig->signal_received = sig;
   multi_close_instance_on_signal (m, mi);
 }
 
 /*
  * Management subsystem callbacks
  */
 
 #ifdef ENABLE_MANAGEMENT
 
 static void
 management_callback_status (void *arg, const int version, struct status_output *so)
 {
   struct multi_context *m = (struct multi_context *) arg;
 
   if (!version)
     multi_print_status (m, so, m->status_file_version);
   else
     multi_print_status (m, so, version);
 }
 
 static int
 management_callback_kill_by_cn (void *arg, const char *del_cn)
 {
   struct multi_context *m = (struct multi_context *) arg;
   struct hash_iterator hi;
   struct hash_element *he;
   int count = 0;
 
   hash_iterator_init (m->iter, &hi, true);
   while ((he = hash_iterator_next (&hi)))
     {
       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;
 	    }
 	}
     }
   hash_iterator_free (&hi);
   return count;
 }
 
 static int
 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;
8bc93d7f
   struct openvpn_sockaddr saddr;
6fbf66fa
   struct mroute_addr maddr;
   int count = 0;
 
   CLEAR (saddr);
8bc93d7f
   saddr.sa.sin_family = AF_INET;
   saddr.sa.sin_addr.s_addr = htonl (addr);
   saddr.sa.sin_port = htons (port);
   if (mroute_extract_openvpn_sockaddr (&maddr, &saddr, true))
6fbf66fa
     {
       hash_iterator_init (m->iter, &hi, true);
       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;
 }
 
 static void
 management_delete_event (void *arg, event_t event)
 {
   struct multi_context *m = (struct multi_context *) arg;
   if (m->mtcp)
     multi_tcp_delete_event (m->mtcp, event);
 }
 
 #endif
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
 
 static struct multi_instance *
 lookup_by_cid (struct multi_context *m, const unsigned long cid)
 {
   if (m)
     {
       struct multi_instance *mi = (struct multi_instance *) hash_lookup (m->cid_hash, &cid);
       if (mi && !mi->halt)
 	return mi;
     }
   return NULL;
 }
 
 static bool
 management_kill_by_cid (void *arg, const unsigned long cid)
 {
   struct multi_context *m = (struct multi_context *) arg;
   struct multi_instance *mi = lookup_by_cid (m, cid);
   if (mi)
     {
       multi_signal_instance (m, mi, SIGTERM);
       return true;
     }
   else
     return false;
 }
 
 static bool
 management_client_auth (void *arg,
 			const unsigned long cid,
 			const unsigned int mda_key_id,
 			const bool auth,
 			const char *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);
       if (ret)
 	{
 	  if (auth && !mi->connection_established_flag)
 	    {
 	      set_cc_config (mi, cc_config);
 	      cc_config_owned = false;
 	    }
 	  if (!auth && reason)
 	    msg (D_MULTI_LOW, "MULTI: connection rejected: %s", reason);
 	}
     }
   if (cc_config_owned && cc_config)
     buffer_list_free (cc_config);
   return ret;
 }
 #endif
 
 #ifdef MANAGEMENT_PF
 static bool
 management_client_pf (void *arg,
 		      const unsigned long cid,
 		      struct buffer_list *pf_config) /* ownership transferred */
 {
   struct multi_context *m = (struct multi_context *) arg;
   struct multi_instance *mi = lookup_by_cid (m, cid);
   bool ret = false;
 
   if (mi && pf_config)
     ret = pf_load_from_buffer_list (&mi->context, pf_config);
 
   if (pf_config)
     buffer_list_free (pf_config);
   return ret;
 }
 #endif
 
6fbf66fa
 void
 init_management_callback_multi (struct multi_context *m)
 {
 #ifdef ENABLE_MANAGEMENT
   if (management)
     {
       struct management_callback cb;
       CLEAR (cb);
       cb.arg = m;
       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;
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
       cb.kill_by_cid = management_kill_by_cid;
       cb.client_auth = management_client_auth;
 #endif
 #ifdef MANAGEMENT_PF
       cb.client_pf = management_client_pf;
 #endif
6fbf66fa
       management_set_callback (management, &cb);
     }
 #endif
 }
 
 void
 uninit_management_callback_multi (struct multi_context *m)
 {
   uninit_management_callback ();
 }
 
 /*
  * Top level event loop.
  */
 void
 tunnel_server (struct context *top)
 {
   ASSERT (top->options.mode == MODE_SERVER);
 
4e9a51d7
   switch (top->options.ce.proto) {
6fbf66fa
   case PROTO_UDPv4:
     tunnel_server_udp (top);
     break;
   case PROTO_TCPv4_SERVER:
     tunnel_server_tcp (top);
     break;
   default:
     ASSERT (0);
   }
 }
 
 #else
 static void dummy(void) {}
 #endif /* P2MP_SERVER */