src/openvpn/mtcp.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
 
6fbf66fa
 #include "syshead.h"
 
 #if P2MP_SERVER
 
 #include "multi.h"
 #include "forward-inline.h"
 
 #include "memdbg.h"
 
4e37af92
 #ifdef HAVE_SYS_INOTIFY_H
 #include <sys/inotify.h>
 #endif
 
6fbf66fa
 /*
  * TCP States
  */
 #define TA_UNDEF                 0
 #define TA_SOCKET_READ           1
 #define TA_SOCKET_READ_RESIDUAL  2
 #define TA_SOCKET_WRITE          3
 #define TA_SOCKET_WRITE_READY    4
 #define TA_SOCKET_WRITE_DEFERRED 5
 #define TA_TUN_READ              6
 #define TA_TUN_WRITE             7
 #define TA_INITIAL               8
 #define TA_TIMEOUT               9
 #define TA_TUN_WRITE_TIMEOUT     10
 
 /*
  * Special tags passed to event.[ch] functions
  */
81d882d5
 #define MTCP_SOCKET      ((void *)1)
 #define MTCP_TUN         ((void *)2)
 #define MTCP_SIG         ((void *)3) /* Only on Windows */
6fbf66fa
 #ifdef ENABLE_MANAGEMENT
81d882d5
 #define MTCP_MANAGEMENT ((void *)4)
6fbf66fa
 #endif
 
0d1a75bf
 #ifdef ENABLE_ASYNC_PUSH
81d882d5
 #define MTCP_FILE_CLOSE_WRITE ((void *)5)
0d1a75bf
 #endif
 
81d882d5
 #define MTCP_N           ((void *)16) /* upper bound on MTCP_x */
6fbf66fa
 
 struct ta_iow_flags
 {
81d882d5
     unsigned int flags;
     unsigned int ret;
     unsigned int tun;
     unsigned int sock;
6fbf66fa
 };
 
 static const char *
81d882d5
 pract(int action)
6fbf66fa
 {
81d882d5
     switch (action)
6fbf66fa
     {
81d882d5
         case TA_UNDEF:
             return "TA_UNDEF";
 
         case TA_SOCKET_READ:
             return "TA_SOCKET_READ";
 
         case TA_SOCKET_READ_RESIDUAL:
             return "TA_SOCKET_READ_RESIDUAL";
 
         case TA_SOCKET_WRITE:
             return "TA_SOCKET_WRITE";
 
         case TA_SOCKET_WRITE_READY:
             return "TA_SOCKET_WRITE_READY";
 
         case TA_SOCKET_WRITE_DEFERRED:
             return "TA_SOCKET_WRITE_DEFERRED";
 
         case TA_TUN_READ:
             return "TA_TUN_READ";
 
         case TA_TUN_WRITE:
             return "TA_TUN_WRITE";
 
         case TA_INITIAL:
             return "TA_INITIAL";
 
         case TA_TIMEOUT:
             return "TA_TIMEOUT";
 
         case TA_TUN_WRITE_TIMEOUT:
             return "TA_TUN_WRITE_TIMEOUT";
 
         default:
             return "?";
6fbf66fa
     }
 }
 
 static struct multi_instance *
81d882d5
 multi_create_instance_tcp(struct multi_context *m)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     struct multi_instance *mi = NULL;
     struct hash *hash = m->hash;
6fbf66fa
 
81d882d5
     mi = multi_create_instance(m, NULL);
     if (mi)
6fbf66fa
     {
81d882d5
         struct hash_element *he;
         const uint32_t hv = hash_value(hash, &mi->real);
         struct hash_bucket *bucket = hash_bucket(hash, hv);
 
         he = hash_lookup_fast(hash, bucket, &mi->real, hv);
 
         if (he)
         {
             struct multi_instance *oldmi = (struct multi_instance *) he->value;
             msg(D_MULTI_LOW, "MULTI TCP: new incoming client address matches existing client address -- new client takes precedence");
             oldmi->did_real_hash = false;
             multi_close_instance(m, oldmi, false);
             he->key = &mi->real;
             he->value = mi;
         }
         else
         {
             hash_add_fast(hash, bucket, &mi->real, hv, mi);
         }
 
         mi->did_real_hash = true;
6fbf66fa
     }
 
 #ifdef ENABLE_DEBUG
81d882d5
     if (mi)
     {
         dmsg(D_MULTI_DEBUG, "MULTI TCP: instance added: %s", mroute_addr_print(&mi->real, &gc));
     }
     else
     {
         dmsg(D_MULTI_DEBUG, "MULTI TCP: new client instance failed");
     }
6fbf66fa
 #endif
 
81d882d5
     gc_free(&gc);
     ASSERT(!(mi && mi->halt));
     return mi;
6fbf66fa
 }
 
 bool
81d882d5
 multi_tcp_instance_specific_init(struct multi_context *m, struct multi_instance *mi)
6fbf66fa
 {
81d882d5
     /* buffer for queued TCP socket output packets */
     mi->tcp_link_out_deferred = mbuf_init(m->top.options.n_bcast_buf);
 
     ASSERT(mi->context.c2.link_socket);
     ASSERT(mi->context.c2.link_socket->info.lsa);
     ASSERT(mi->context.c2.link_socket->mode == LS_MODE_TCP_ACCEPT_FROM);
     ASSERT(mi->context.c2.link_socket->info.lsa->actual.dest.addr.sa.sa_family == AF_INET
            || mi->context.c2.link_socket->info.lsa->actual.dest.addr.sa.sa_family == AF_INET6
            );
     if (!mroute_extract_openvpn_sockaddr(&mi->real, &mi->context.c2.link_socket->info.lsa->actual.dest, true))
6fbf66fa
     {
81d882d5
         msg(D_MULTI_ERRORS, "MULTI TCP: TCP client address is undefined");
         return false;
6fbf66fa
     }
81d882d5
     return true;
6fbf66fa
 }
 
 void
81d882d5
 multi_tcp_instance_specific_free(struct multi_instance *mi)
6fbf66fa
 {
81d882d5
     mbuf_free(mi->tcp_link_out_deferred);
6fbf66fa
 }
 
 struct multi_tcp *
81d882d5
 multi_tcp_init(int maxevents, int *maxclients)
6fbf66fa
 {
81d882d5
     struct multi_tcp *mtcp;
     const int extra_events = BASE_N_EVENTS;
 
     ASSERT(maxevents >= 1);
     ASSERT(maxclients);
 
     ALLOC_OBJ_CLEAR(mtcp, struct multi_tcp);
     mtcp->maxevents = maxevents + extra_events;
     mtcp->es = event_set_init(&mtcp->maxevents, 0);
     wait_signal(mtcp->es, MTCP_SIG);
     ALLOC_ARRAY(mtcp->esr, struct event_set_return, mtcp->maxevents);
     *maxclients = max_int(min_int(mtcp->maxevents - extra_events, *maxclients), 1);
     msg(D_MULTI_LOW, "MULTI: TCP INIT maxclients=%d maxevents=%d", *maxclients, mtcp->maxevents);
     return mtcp;
6fbf66fa
 }
 
 void
81d882d5
 multi_tcp_delete_event(struct multi_tcp *mtcp, event_t event)
6fbf66fa
 {
81d882d5
     if (mtcp && mtcp->es)
     {
         event_del(mtcp->es, event);
     }
6fbf66fa
 }
 
 void
81d882d5
 multi_tcp_free(struct multi_tcp *mtcp)
6fbf66fa
 {
81d882d5
     if (mtcp)
6fbf66fa
     {
81d882d5
         event_free(mtcp->es);
         if (mtcp->esr)
         {
             free(mtcp->esr);
         }
         free(mtcp);
6fbf66fa
     }
 }
 
 void
81d882d5
 multi_tcp_dereference_instance(struct multi_tcp *mtcp, struct multi_instance *mi)
6fbf66fa
 {
81d882d5
     struct link_socket *ls = mi->context.c2.link_socket;
     if (ls && mi->socket_set_called)
     {
         event_del(mtcp->es, socket_event_handle(ls));
     }
     mtcp->n_esr = 0;
6fbf66fa
 }
 
 static inline void
81d882d5
 multi_tcp_set_global_rw_flags(struct multi_context *m, struct multi_instance *mi)
6fbf66fa
 {
81d882d5
     if (mi)
6fbf66fa
     {
81d882d5
         mi->socket_set_called = true;
         socket_set(mi->context.c2.link_socket,
                    m->mtcp->es,
                    mbuf_defined(mi->tcp_link_out_deferred) ? EVENT_WRITE : EVENT_READ,
                    mi,
                    &mi->tcp_rwflags);
6fbf66fa
     }
 }
 
 static inline int
81d882d5
 multi_tcp_wait(const struct context *c,
                struct multi_tcp *mtcp)
6fbf66fa
 {
81d882d5
     int status;
     socket_set_listen_persistent(c->c2.link_socket, mtcp->es, MTCP_SOCKET);
     tun_set(c->c1.tuntap, mtcp->es, EVENT_READ, MTCP_TUN, &mtcp->tun_rwflags);
6fbf66fa
 #ifdef ENABLE_MANAGEMENT
81d882d5
     if (management)
     {
         management_socket_set(management, mtcp->es, MTCP_MANAGEMENT, &mtcp->management_persist_flags);
     }
6fbf66fa
 #endif
0d1a75bf
 
 #ifdef ENABLE_ASYNC_PUSH
81d882d5
     /* arm inotify watcher */
     event_ctl(mtcp->es, c->c2.inotify_fd, EVENT_READ, MTCP_FILE_CLOSE_WRITE);
0d1a75bf
 #endif
 
81d882d5
     status = event_wait(mtcp->es, &c->c2.timeval, mtcp->esr, mtcp->maxevents);
     update_time();
     mtcp->n_esr = 0;
     if (status > 0)
     {
         mtcp->n_esr = status;
     }
     return status;
6fbf66fa
 }
 
 static inline struct context *
81d882d5
 multi_tcp_context(struct multi_context *m, struct multi_instance *mi)
6fbf66fa
 {
81d882d5
     if (mi)
     {
         return &mi->context;
     }
     else
     {
         return &m->top;
     }
6fbf66fa
 }
 
 static bool
81d882d5
 multi_tcp_process_outgoing_link_ready(struct multi_context *m, struct multi_instance *mi, const unsigned int mpp_flags)
6fbf66fa
 {
81d882d5
     struct mbuf_item item;
     bool ret = true;
     ASSERT(mi);
6fbf66fa
 
81d882d5
     /* extract from queue */
     if (mbuf_extract_item(mi->tcp_link_out_deferred, &item)) /* ciphertext IP packet */
6fbf66fa
     {
81d882d5
         dmsg(D_MULTI_TCP, "MULTI TCP: transmitting previously deferred packet");
 
         ASSERT(mi == item.instance);
         mi->context.c2.to_link = item.buffer->buf;
         ret = multi_process_outgoing_link_dowork(m, mi, mpp_flags);
         if (!ret)
         {
             mi = NULL;
         }
         mbuf_free_buf(item.buffer);
6fbf66fa
     }
81d882d5
     return ret;
6fbf66fa
 }
 
 static bool
81d882d5
 multi_tcp_process_outgoing_link(struct multi_context *m, bool defer, const unsigned int mpp_flags)
6fbf66fa
 {
81d882d5
     struct multi_instance *mi = multi_process_outgoing_link_pre(m);
     bool ret = true;
6fbf66fa
 
81d882d5
     if (mi)
6fbf66fa
     {
81d882d5
         if (defer || mbuf_defined(mi->tcp_link_out_deferred))
         {
             /* save to queue */
             struct buffer *buf = &mi->context.c2.to_link;
             if (BLEN(buf) > 0)
             {
                 struct mbuf_buffer *mb = mbuf_alloc_buf(buf);
                 struct mbuf_item item;
 
                 set_prefix(mi);
                 dmsg(D_MULTI_TCP, "MULTI TCP: queuing deferred packet");
                 item.buffer = mb;
                 item.instance = mi;
                 mbuf_add_item(mi->tcp_link_out_deferred, &item);
                 mbuf_free_buf(mb);
                 buf_reset(buf);
                 ret = multi_process_post(m, mi, mpp_flags);
                 if (!ret)
                 {
                     mi = NULL;
                 }
                 clear_prefix();
             }
         }
         else
         {
             ret = multi_process_outgoing_link_dowork(m, mi, mpp_flags);
             if (!ret)
             {
                 mi = NULL;
             }
         }
6fbf66fa
     }
81d882d5
     return ret;
6fbf66fa
 }
 
 static int
81d882d5
 multi_tcp_wait_lite(struct multi_context *m, struct multi_instance *mi, const int action, bool *tun_input_pending)
6fbf66fa
 {
81d882d5
     struct context *c = multi_tcp_context(m, mi);
     unsigned int looking_for = 0;
6fbf66fa
 
81d882d5
     dmsg(D_MULTI_DEBUG, "MULTI TCP: multi_tcp_wait_lite a=%s mi=" ptr_format,
          pract(action),
          (ptr_type)mi);
6fbf66fa
 
81d882d5
     tv_clear(&c->c2.timeval); /* ZERO-TIMEOUT */
6fbf66fa
 
81d882d5
     switch (action)
6fbf66fa
     {
81d882d5
         case TA_TUN_READ:
             looking_for = TUN_READ;
             tun_input_pending = NULL;
             io_wait(c, IOW_READ_TUN);
             break;
 
         case TA_SOCKET_READ:
             looking_for = SOCKET_READ;
             tun_input_pending = NULL;
             io_wait(c, IOW_READ_LINK);
             break;
 
         case TA_TUN_WRITE:
             looking_for = TUN_WRITE;
             tun_input_pending = NULL;
             c->c2.timeval.tv_sec = 1; /* For some reason, the Linux 2.2 TUN/TAP driver hits this timeout */
             perf_push(PERF_PROC_OUT_TUN_MTCP);
             io_wait(c, IOW_TO_TUN);
             perf_pop();
             break;
 
         case TA_SOCKET_WRITE:
             looking_for = SOCKET_WRITE;
             io_wait(c, IOW_TO_LINK|IOW_READ_TUN_FORCE);
             break;
 
         default:
             msg(M_FATAL, "MULTI TCP: multi_tcp_wait_lite, unhandled action=%d", action);
6fbf66fa
     }
 
81d882d5
     if (tun_input_pending && (c->c2.event_set_status & TUN_READ))
     {
         *tun_input_pending = true;
     }
6fbf66fa
 
81d882d5
     if (c->c2.event_set_status & looking_for)
6fbf66fa
     {
81d882d5
         return action;
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         switch (action)
         {
             /* TCP socket output buffer is full */
             case TA_SOCKET_WRITE:
                 return TA_SOCKET_WRITE_DEFERRED;
 
             /* TUN device timed out on accepting write */
             case TA_TUN_WRITE:
                 return TA_TUN_WRITE_TIMEOUT;
         }
 
         return TA_UNDEF;
6fbf66fa
     }
 }
 
 static struct multi_instance *
81d882d5
 multi_tcp_dispatch(struct multi_context *m, struct multi_instance *mi, const int action)
6fbf66fa
 {
81d882d5
     const unsigned int mpp_flags = MPP_PRE_SELECT|MPP_RECORD_TOUCH;
     struct multi_instance *touched = mi;
     m->mpp_touched = &touched;
6fbf66fa
 
81d882d5
     dmsg(D_MULTI_DEBUG, "MULTI TCP: multi_tcp_dispatch a=%s mi=" ptr_format,
          pract(action),
          (ptr_type)mi);
6fbf66fa
 
81d882d5
     switch (action)
6fbf66fa
     {
81d882d5
         case TA_TUN_READ:
             read_incoming_tun(&m->top);
             if (!IS_SIG(&m->top))
             {
                 multi_process_incoming_tun(m, mpp_flags);
             }
             break;
 
         case TA_SOCKET_READ:
         case TA_SOCKET_READ_RESIDUAL:
             ASSERT(mi);
             ASSERT(mi->context.c2.link_socket);
             set_prefix(mi);
             read_incoming_link(&mi->context);
             clear_prefix();
             if (!IS_SIG(&mi->context))
             {
                 multi_process_incoming_link(m, mi, mpp_flags);
                 if (!IS_SIG(&mi->context))
                 {
                     stream_buf_read_setup(mi->context.c2.link_socket);
                 }
             }
             break;
 
         case TA_TIMEOUT:
             multi_process_timeout(m, mpp_flags);
             break;
 
         case TA_TUN_WRITE:
             multi_process_outgoing_tun(m, mpp_flags);
             break;
 
         case TA_TUN_WRITE_TIMEOUT:
             multi_process_drop_outgoing_tun(m, mpp_flags);
             break;
 
         case TA_SOCKET_WRITE_READY:
             ASSERT(mi);
             multi_tcp_process_outgoing_link_ready(m, mi, mpp_flags);
             break;
 
         case TA_SOCKET_WRITE:
             multi_tcp_process_outgoing_link(m, false, mpp_flags);
             break;
 
         case TA_SOCKET_WRITE_DEFERRED:
             multi_tcp_process_outgoing_link(m, true, mpp_flags);
             break;
 
         case TA_INITIAL:
             ASSERT(mi);
             multi_tcp_set_global_rw_flags(m, mi);
             multi_process_post(m, mi, mpp_flags);
             break;
 
         default:
             msg(M_FATAL, "MULTI TCP: multi_tcp_dispatch, unhandled action=%d", action);
6fbf66fa
     }
 
81d882d5
     m->mpp_touched = NULL;
     return touched;
6fbf66fa
 }
 
72bcdfdc
 static int
81d882d5
 multi_tcp_post(struct multi_context *m, struct multi_instance *mi, const int action)
6fbf66fa
 {
81d882d5
     struct context *c = multi_tcp_context(m, mi);
     int newaction = TA_UNDEF;
6fbf66fa
 
81d882d5
 #define MTP_NONE         0
 #define MTP_TUN_OUT      (1<<0)
 #define MTP_LINK_OUT     (1<<1)
     unsigned int flags = MTP_NONE;
6fbf66fa
 
81d882d5
     if (TUN_OUT(c))
     {
         flags |= MTP_TUN_OUT;
     }
     if (LINK_OUT(c))
     {
         flags |= MTP_LINK_OUT;
     }
6fbf66fa
 
81d882d5
     switch (flags)
6fbf66fa
     {
81d882d5
         case MTP_TUN_OUT|MTP_LINK_OUT:
         case MTP_TUN_OUT:
             newaction = TA_TUN_WRITE;
             break;
 
         case MTP_LINK_OUT:
             newaction = TA_SOCKET_WRITE;
             break;
 
         case MTP_NONE:
             if (mi && socket_read_residual(c->c2.link_socket))
             {
                 newaction = TA_SOCKET_READ_RESIDUAL;
             }
             else
             {
                 multi_tcp_set_global_rw_flags(m, mi);
             }
             break;
 
         default:
         {
             struct gc_arena gc = gc_new();
             msg(M_FATAL, "MULTI TCP: multi_tcp_post bad state, mi=%s flags=%d",
                 multi_instance_string(mi, false, &gc),
                 flags);
             gc_free(&gc);
             break;
         }
6fbf66fa
     }
 
81d882d5
     dmsg(D_MULTI_DEBUG, "MULTI TCP: multi_tcp_post %s -> %s",
          pract(action),
          pract(newaction));
6fbf66fa
 
81d882d5
     return newaction;
6fbf66fa
 }
 
 static void
81d882d5
 multi_tcp_action(struct multi_context *m, struct multi_instance *mi, int action, bool poll)
6fbf66fa
 {
81d882d5
     bool tun_input_pending = false;
 
4cd4899e
     do
     {
81d882d5
         dmsg(D_MULTI_DEBUG, "MULTI TCP: multi_tcp_action a=%s p=%d",
              pract(action),
              poll);
 
         /*
          * If TA_SOCKET_READ_RESIDUAL, it means we still have pending
          * input packets which were read by a prior TCP recv.
          *
          * Otherwise do a "lite" wait, which means we wait with 0 timeout
          * on I/O events only related to the current instance, not
          * the big list of events.
          *
          * On our first pass, poll will be false because we already know
          * that input is available, and to call io_wait would be redundant.
          */
         if (poll && action != TA_SOCKET_READ_RESIDUAL)
         {
             const int orig_action = action;
             action = multi_tcp_wait_lite(m, mi, action, &tun_input_pending);
             if (action == TA_UNDEF)
             {
                 msg(M_FATAL, "MULTI TCP: I/O wait required blocking in multi_tcp_action, action=%d", orig_action);
             }
         }
 
         /*
          * Dispatch the action
          */
         {
             struct multi_instance *touched = multi_tcp_dispatch(m, mi, action);
 
             /*
              * Signal received or TCP connection
              * reset by peer?
              */
             if (touched && IS_SIG(&touched->context))
             {
                 if (mi == touched)
                 {
                     mi = NULL;
                 }
                 multi_close_instance_on_signal(m, touched);
             }
         }
 
         /*
          * If dispatch produced any pending output
          * for a particular instance, point to
          * that instance.
          */
         if (m->pending)
         {
             mi = m->pending;
         }
 
         /*
          * Based on the effects of the action,
          * such as generating pending output,
          * possibly transition to a new action state.
          */
         action = multi_tcp_post(m, mi, action);
 
         /*
          * If we are finished processing the original action,
          * check if we have any TUN input.  If so, transition
          * our action state to processing this input.
          */
         if (tun_input_pending && action == TA_UNDEF)
         {
             action = TA_TUN_READ;
             mi = NULL;
             tun_input_pending = false;
             poll = false;
         }
         else
         {
             poll = true;
         }
 
     } while (action != TA_UNDEF);
6fbf66fa
 }
 
 static void
81d882d5
 multi_tcp_process_io(struct multi_context *m)
6fbf66fa
 {
81d882d5
     struct multi_tcp *mtcp = m->mtcp;
     int i;
6fbf66fa
 
81d882d5
     for (i = 0; i < mtcp->n_esr; ++i)
6fbf66fa
     {
81d882d5
         struct event_set_return *e = &mtcp->esr[i];
 
         /* incoming data for instance? */
         if (e->arg >= MTCP_N)
         {
             struct multi_instance *mi = (struct multi_instance *) e->arg;
             if (mi)
             {
                 if (e->rwflags & EVENT_WRITE)
                 {
                     multi_tcp_action(m, mi, TA_SOCKET_WRITE_READY, false);
                 }
                 else if (e->rwflags & EVENT_READ)
                 {
                     multi_tcp_action(m, mi, TA_SOCKET_READ, false);
                 }
             }
         }
         else
         {
6fbf66fa
 #ifdef ENABLE_MANAGEMENT
81d882d5
             if (e->arg == MTCP_MANAGEMENT)
             {
                 ASSERT(management);
                 management_io(management);
             }
             else
6fbf66fa
 #endif
81d882d5
             /* incoming data on TUN? */
             if (e->arg == MTCP_TUN)
             {
                 if (e->rwflags & EVENT_WRITE)
                 {
                     multi_tcp_action(m, NULL, TA_TUN_WRITE, false);
                 }
                 else if (e->rwflags & EVENT_READ)
                 {
                     multi_tcp_action(m, NULL, TA_TUN_READ, false);
                 }
             }
             /* new incoming TCP client attempting to connect? */
             else if (e->arg == MTCP_SOCKET)
             {
                 struct multi_instance *mi;
                 ASSERT(m->top.c2.link_socket);
                 socket_reset_listen_persistent(m->top.c2.link_socket);
                 mi = multi_create_instance_tcp(m);
                 if (mi)
                 {
                     multi_tcp_action(m, mi, TA_INITIAL, false);
                 }
             }
             /* signal received? */
             else if (e->arg == MTCP_SIG)
             {
                 get_signal(&m->top.sig->signal_received);
             }
0d1a75bf
 #ifdef ENABLE_ASYNC_PUSH
81d882d5
             else if (e->arg == MTCP_FILE_CLOSE_WRITE)
             {
                 multi_process_file_closed(m, MPP_PRE_SELECT | MPP_RECORD_TOUCH);
             }
0d1a75bf
 #endif
81d882d5
         }
         if (IS_SIG(&m->top))
         {
             break;
         }
     }
     mtcp->n_esr = 0;
 
     /*
      * Process queued mbuf packets destined for TCP socket
      */
     {
         struct multi_instance *mi;
         while (!IS_SIG(&m->top) && (mi = mbuf_peek(m->mbuf)) != NULL)
         {
             multi_tcp_action(m, mi, TA_SOCKET_WRITE, true);
         }
6fbf66fa
     }
 }
 
 /*
  * Top level event loop for single-threaded operation.
  * TCP mode.
  */
 void
81d882d5
 tunnel_server_tcp(struct context *top)
6fbf66fa
 {
81d882d5
     struct multi_context multi;
     int status;
6fbf66fa
 
81d882d5
     top->mode = CM_TOP;
     context_clear_2(top);
6fbf66fa
 
81d882d5
     /* initialize top-tunnel instance */
     init_instance_handle_signals(top, top->es, CC_HARD_USR1_TO_HUP);
     if (IS_SIG(top))
     {
         return;
     }
6fbf66fa
 
81d882d5
     /* initialize global multi_context object */
     multi_init(&multi, top, true, MC_SINGLE_THREADED);
6fbf66fa
 
81d882d5
     /* initialize our cloned top object */
     multi_top_init(&multi, top);
6fbf66fa
 
81d882d5
     /* initialize management interface */
     init_management_callback_multi(&multi);
 
     /* finished with initialization */
     initialization_sequence_completed(top, ISC_SERVER); /* --mode server --proto tcp-server */
6fbf66fa
 
0d1a75bf
 #ifdef ENABLE_ASYNC_PUSH
81d882d5
     multi.top.c2.inotify_fd = inotify_init();
     if (multi.top.c2.inotify_fd < 0)
0d1a75bf
     {
56b396dc
         msg(D_MULTI_ERRORS | M_ERRNO, "MULTI: inotify_init error");
0d1a75bf
     }
 #endif
 
81d882d5
     /* per-packet event loop */
     while (true)
6fbf66fa
     {
81d882d5
         perf_push(PERF_EVENT_LOOP);
 
         /* wait on tun/socket list */
         multi_get_timeout(&multi, &multi.top.c2.timeval);
         status = multi_tcp_wait(&multi.top, multi.mtcp);
         MULTI_CHECK_SIG(&multi);
 
         /* check on status of coarse timers */
         multi_process_per_second_timers(&multi);
 
         /* timeout? */
         if (status > 0)
         {
             /* process the I/O which triggered select */
             multi_tcp_process_io(&multi);
             MULTI_CHECK_SIG(&multi);
         }
         else if (status == 0)
         {
             multi_tcp_action(&multi, NULL, TA_TIMEOUT, false);
         }
 
         perf_pop();
6fbf66fa
     }
 
0d1a75bf
 #ifdef ENABLE_ASYNC_PUSH
81d882d5
     close(top->c2.inotify_fd);
0d1a75bf
 #endif
 
81d882d5
     /* shut down management interface */
     uninit_management_callback_multi(&multi);
6fbf66fa
 
81d882d5
     /* save ifconfig-pool */
     multi_ifconfig_pool_persist(&multi, true);
6fbf66fa
 
81d882d5
     /* tear down tunnel instance (unless --persist-tun) */
     multi_uninit(&multi);
     multi_top_free(&multi);
     close_instance(top);
6fbf66fa
 }
 
81d882d5
 #endif /* if P2MP_SERVER */