@@ -455,6 +455,8 @@
src/openvpn/mudp.h
src/openvpn/multi.c
src/openvpn/multi.h
+ src/openvpn/multi_io.h
+ src/openvpn/multi_io.c
src/openvpn/ntlm.c
src/openvpn/ntlm.h
src/openvpn/occ.c
@@ -92,6 +92,7 @@
mtu.c mtu.h \
mudp.c mudp.h \
multi.c multi.h \
+ multi_io.c multi_io.h \
networking_freebsd.c \
networking_iproute2.c networking_iproute2.h \
networking_sitnl.c networking_sitnl.h \
@@ -50,6 +50,7 @@
#include "openvpn.h"
#include "occ.h"
#include "ping.h"
+#include "multi_io.h"
#define IOW_TO_TUN (1<<0)
#define IOW_TO_LINK (1<<1)
@@ -29,6 +29,8 @@
#include "multi.h"
#include "forward.h"
+#include "mtcp.h"
+#include "multi_io.h"
#include "memdbg.h"
@@ -36,31 +38,6 @@
#include <sys/inotify.h>
#endif
-/*
- * 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
- */
-#define MTCP_SOCKET ((void *)1)
-#define MTCP_TUN ((void *)2)
-#define MTCP_SIG ((void *)3) /* Only on Windows */
-#define MTCP_MANAGEMENT ((void *)4)
-#define MTCP_FILE_CLOSE_WRITE ((void *)5)
-#define MTCP_DCO ((void *)6)
-
struct ta_iow_flags
{
unsigned int flags;
@@ -69,52 +46,7 @@
unsigned int sock;
};
-#ifdef ENABLE_DEBUG
-static const char *
-pract(int action)
-{
- switch (action)
- {
- 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 "?";
- }
-}
-#endif /* ENABLE_DEBUG */
-
-static struct multi_instance *
+struct multi_instance *
multi_create_instance_tcp(struct multi_context *m, struct link_socket *ls)
{
struct gc_arena gc = gc_new();
@@ -194,126 +126,42 @@
mbuf_free(mi->tcp_link_out_deferred);
}
-struct multi_tcp *
-multi_tcp_init(int maxevents, int *maxclients)
-{
- 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;
-}
-
void
-multi_tcp_delete_event(struct multi_tcp *mtcp, event_t event)
+multi_tcp_delete_event(struct multi_protocol *multi_io, event_t event)
{
- if (mtcp && mtcp->es)
+ if (multi_io && multi_io->es)
{
- event_del(mtcp->es, event);
+ event_del(multi_io->es, event);
}
}
void
-multi_tcp_free(struct multi_tcp *mtcp)
-{
- if (mtcp)
- {
- event_free(mtcp->es);
- free(mtcp->esr);
- free(mtcp);
- }
-}
-
-void
-multi_tcp_dereference_instance(struct multi_tcp *mtcp, struct multi_instance *mi)
+multi_tcp_dereference_instance(struct multi_protocol *multi_io, struct multi_instance *mi)
{
struct link_socket *ls = mi->context.c2.link_sockets[0];
if (ls && mi->socket_set_called)
{
- event_del(mtcp->es, socket_event_handle(ls));
+ event_del(multi_io->es, socket_event_handle(ls));
mi->socket_set_called = false;
}
- mtcp->n_esr = 0;
+ multi_io->n_esr = 0;
}
-static inline void
+void
multi_tcp_set_global_rw_flags(struct multi_context *m, struct multi_instance *mi)
{
if (mi)
{
mi->socket_set_called = true;
socket_set(mi->context.c2.link_sockets[0],
- m->mtcp->es,
+ m->multi_io->es,
mbuf_defined(mi->tcp_link_out_deferred) ? EVENT_WRITE : EVENT_READ,
&mi->ev_arg,
&mi->tcp_rwflags);
}
}
-static inline int
-multi_tcp_wait(const struct context *c,
- struct multi_tcp *mtcp)
-{
- int status, i;
-
- for (i = 0; i < c->c1.link_sockets_num; i++)
- {
- socket_set_listen_persistent(c->c2.link_sockets[i], mtcp->es,
- &c->c2.link_sockets[i]->ev_arg);
- }
-
-#ifdef _WIN32
- if (tuntap_is_wintun(c->c1.tuntap))
- {
- if (!tuntap_ring_empty(c->c1.tuntap))
- {
- /* there is data in wintun ring buffer, read it immediately */
- mtcp->esr[0].arg = MTCP_TUN;
- mtcp->esr[0].rwflags = EVENT_READ;
- mtcp->n_esr = 1;
- return 1;
- }
- persistent = NULL;
- }
-#endif
-
- tun_set(c->c1.tuntap, mtcp->es, EVENT_READ, MTCP_TUN, &mtcp->tun_rwflags);
-#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
- dco_event_set(&c->c1.tuntap->dco, mtcp->es, MTCP_DCO);
-#endif
-
-#ifdef ENABLE_MANAGEMENT
- if (management)
- {
- management_socket_set(management, mtcp->es, MTCP_MANAGEMENT, &mtcp->management_persist_flags);
- }
-#endif
-
-#ifdef ENABLE_ASYNC_PUSH
- /* arm inotify watcher */
- event_ctl(mtcp->es, c->c2.inotify_fd, EVENT_READ, MTCP_FILE_CLOSE_WRITE);
-#endif
-
- 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;
-}
-
-static inline struct context *
+struct context *
multi_tcp_context(struct multi_context *m, struct multi_instance *mi)
{
if (mi)
@@ -326,7 +174,7 @@
}
}
-static bool
+bool
multi_tcp_process_outgoing_link_ready(struct multi_context *m, struct multi_instance *mi, const unsigned int mpp_flags)
{
struct mbuf_item item;
@@ -350,7 +198,7 @@
return ret;
}
-static bool
+bool
multi_tcp_process_outgoing_link(struct multi_context *m, bool defer, const unsigned int mpp_flags)
{
struct multi_instance *mi = multi_process_outgoing_link_pre(m);
@@ -394,429 +242,6 @@
return ret;
}
-static int
-multi_tcp_wait_lite(struct multi_context *m, struct multi_instance *mi, const int action, bool *tun_input_pending)
-{
- struct context *c = multi_tcp_context(m, mi);
- unsigned int looking_for = 0;
-
- dmsg(D_MULTI_DEBUG, "MULTI TCP: multi_tcp_wait_lite a=%s mi=" ptr_format,
- pract(action),
- (ptr_type)mi);
-
- tv_clear(&c->c2.timeval); /* ZERO-TIMEOUT */
-
- switch (action)
- {
- 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);
- }
-
- if (tun_input_pending && (c->c2.event_set_status & TUN_READ))
- {
- *tun_input_pending = true;
- }
-
- if (c->c2.event_set_status & looking_for)
- {
- return action;
- }
- else
- {
- 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;
- }
-}
-
-static struct multi_instance *
-multi_tcp_dispatch(struct multi_context *m, struct multi_instance *mi, const int action)
-{
- const unsigned int mpp_flags = MPP_PRE_SELECT|MPP_RECORD_TOUCH;
- struct multi_instance *touched = mi;
- m->mpp_touched = &touched;
-
- dmsg(D_MULTI_DEBUG, "MULTI TCP: multi_tcp_dispatch a=%s mi=" ptr_format,
- pract(action),
- (ptr_type)mi);
-
- switch (action)
- {
- 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_sockets);
- ASSERT(mi->context.c2.link_sockets[0]);
- set_prefix(mi);
- read_incoming_link(&mi->context, mi->context.c2.link_sockets[0]);
- clear_prefix();
- if (!IS_SIG(&mi->context))
- {
- multi_process_incoming_link(m, mi, mpp_flags,
- mi->context.c2.link_sockets[0]);
- if (!IS_SIG(&mi->context))
- {
- stream_buf_read_setup(mi->context.c2.link_sockets[0]);
- }
- }
- 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);
- }
-
- m->mpp_touched = NULL;
- return touched;
-}
-
-static int
-multi_tcp_post(struct multi_context *m, struct multi_instance *mi, const int action)
-{
- struct context *c = multi_tcp_context(m, mi);
- int newaction = TA_UNDEF;
-
-#define MTP_NONE 0
-#define MTP_TUN_OUT (1<<0)
-#define MTP_LINK_OUT (1<<1)
- unsigned int flags = MTP_NONE;
-
- if (TUN_OUT(c))
- {
- flags |= MTP_TUN_OUT;
- }
- if (LINK_OUT(c))
- {
- flags |= MTP_LINK_OUT;
- }
-
- switch (flags)
- {
- 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 && sockets_read_residual(c))
- {
- 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;
- }
- }
-
- dmsg(D_MULTI_DEBUG, "MULTI TCP: multi_tcp_post %s -> %s",
- pract(action),
- pract(newaction));
-
- return newaction;
-}
-
-static void
-multi_tcp_action(struct multi_context *m, struct multi_instance *mi, int action, bool poll)
-{
- bool tun_input_pending = false;
-
- do
- {
- 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);
-}
-
-static void
-multi_tcp_process_io(struct multi_context *m)
-{
- struct multi_tcp *mtcp = m->mtcp;
- int i;
-
- for (i = 0; i < mtcp->n_esr; ++i)
- {
- struct event_set_return *e = &mtcp->esr[i];
- struct event_arg *ev_arg = (struct event_arg *)e->arg;
-
- /* incoming data for instance or listening socket? */
- if (e->arg >= MULTI_N)
- {
- switch (ev_arg->type)
- {
- struct multi_instance *mi;
-
- /* react to event on child instance */
- case EVENT_ARG_MULTI_INSTANCE:
- if (!ev_arg->u.mi)
- {
- msg(D_MULTI_ERRORS, "MULTI: mtcp_proc_io: null minstance");
- break;
- }
-
- mi = ev_arg->u.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);
- }
- break;
-
- /* new incoming TCP client attempting to connect? */
- case EVENT_ARG_LINK_SOCKET:
- if (!ev_arg->u.ls)
- {
- msg(D_MULTI_ERRORS, "MULTI: mtcp_proc_io: null socket");
- break;
- }
-
- socket_reset_listen_persistent(ev_arg->u.ls);
- mi = multi_create_instance_tcp(m, ev_arg->u.ls);
- if (mi)
- {
- multi_tcp_action(m, mi, TA_INITIAL, false);
- }
- break;
- }
- }
- else
- {
-#ifdef ENABLE_MANAGEMENT
- if (e->arg == MTCP_MANAGEMENT)
- {
- ASSERT(management);
- management_io(management);
- }
- else
-#endif
- /* 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_sockets[0]);
- socket_reset_listen_persistent(m->top.c2.link_sockets[0]);
- mi = multi_create_instance_tcp(m, m->top.c2.link_sockets[0]);
- if (mi)
- {
- multi_tcp_action(m, mi, TA_INITIAL, false);
- }
- }
-#if defined(ENABLE_DCO) && (defined(TARGET_LINUX) || defined(TARGET_FREEBSD))
- /* incoming data on DCO? */
- else if (e->arg == MTCP_DCO)
- {
- multi_process_incoming_dco(m);
- }
-#endif
- /* signal received? */
- else if (e->arg == MTCP_SIG)
- {
- get_signal(&m->top.sig->signal_received);
- }
-#ifdef ENABLE_ASYNC_PUSH
- else if (e->arg == MTCP_FILE_CLOSE_WRITE)
- {
- multi_process_file_closed(m, MPP_PRE_SELECT | MPP_RECORD_TOUCH);
- }
-#endif
- }
- 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);
- }
- }
-}
-
/*
* Top level event loop for single-threaded operation.
* TCP mode.
@@ -864,7 +289,7 @@
/* wait on tun/socket list */
multi_get_timeout(&multi, &multi.top.c2.timeval);
- status = multi_tcp_wait(&multi.top, multi.mtcp);
+ status = multi_protocol_io_wait(&multi);
MULTI_CHECK_SIG(&multi);
/* check on status of coarse timers */
@@ -874,12 +299,12 @@
if (status > 0)
{
/* process the I/O which triggered select */
- multi_tcp_process_io(&multi);
+ multi_protocol_process_io(&multi);
MULTI_CHECK_SIG(&multi);
}
else if (status == 0)
{
- multi_tcp_action(&multi, NULL, TA_TIMEOUT, false);
+ multi_protocol_action(&multi, NULL, TA_TIMEOUT, false);
}
perf_pop();
@@ -30,34 +30,25 @@
#include "event.h"
-/*
- * Extra state info needed for TCP mode
- */
-struct multi_tcp
-{
- struct event_set *es;
- struct event_set_return *esr;
- int n_esr;
- int maxevents;
- unsigned int tun_rwflags;
-#ifdef ENABLE_MANAGEMENT
- unsigned int management_persist_flags;
-#endif
-};
-
struct multi_instance;
struct context;
-struct multi_tcp *multi_tcp_init(int maxevents, int *maxclients);
-
-void multi_tcp_free(struct multi_tcp *mtcp);
-
-void multi_tcp_dereference_instance(struct multi_tcp *mtcp, struct multi_instance *mi);
+void multi_tcp_dereference_instance(struct multi_protocol *multi_io, struct multi_instance *mi);
bool multi_tcp_instance_specific_init(struct multi_context *m, struct multi_instance *mi);
void multi_tcp_instance_specific_free(struct multi_instance *mi);
+struct context *multi_tcp_context(struct multi_context *m, struct multi_instance *mi);
+
+void multi_tcp_set_global_rw_flags(struct multi_context *m, struct multi_instance *mi);
+
+bool multi_tcp_process_outgoing_link(struct multi_context *m, bool defer, const unsigned int mpp_flags);
+
+bool multi_tcp_process_outgoing_link_ready(struct multi_context *m, struct multi_instance *mi, const unsigned int mpp_flags);
+
+struct multi_instance *multi_create_instance_tcp(struct multi_context *m, struct link_socket *ls);
+
void multi_tcp_link_out_deferred(struct multi_context *m, struct multi_instance *mi);
@@ -71,6 +62,6 @@
void tunnel_server_tcp(struct context *top);
-void multi_tcp_delete_event(struct multi_tcp *mtcp, event_t event);
+void multi_tcp_delete_event(struct multi_protocol *multi_io, event_t event);
#endif /* ifndef MTCP_H */
@@ -440,7 +440,7 @@
*/
if (tcp_mode)
{
- m->mtcp = multi_tcp_init(t->options.max_clients, &m->max_clients);
+ m->multi_io = multi_protocol_init(t->options.max_clients, &m->max_clients);
}
m->tcp_queue_limit = t->options.tcp_queue_limit;
@@ -665,9 +665,9 @@
mi->did_iroutes = false;
}
- if (m->mtcp)
+ if (m->multi_io)
{
- multi_tcp_dereference_instance(m->mtcp, mi);
+ multi_tcp_dereference_instance(m->multi_io, mi);
}
mbuf_dereference_instance(m->mbuf, mi);
@@ -745,7 +745,7 @@
initial_rate_limit_free(m->initial_rate_limiter);
multi_reap_free(m->reaper);
mroute_helper_free(m->route_helper);
- multi_tcp_free(m->mtcp);
+ multi_protocol_free(m->multi_io);
}
}
@@ -3978,9 +3978,9 @@
management_delete_event(void *arg, event_t event)
{
struct multi_context *m = (struct multi_context *) arg;
- if (m->mtcp)
+ if (m->multi_io)
{
- multi_tcp_delete_event(m->mtcp, event);
+ multi_tcp_delete_event(m->multi_io, event);
}
}
@@ -37,6 +37,7 @@
#include "pool.h"
#include "mudp.h"
#include "mtcp.h"
+#include "multi_io.h"
#include "perf.h"
#include "vlan.h"
#include "reflect_filter.h"
@@ -169,8 +170,8 @@
struct mbuf_set *mbuf; /**< Set of buffers for passing data
* channel packets between VPN tunnel
* instances. */
- struct multi_tcp *mtcp; /**< State specific to OpenVPN using TCP
- * as external transport. */
+ struct multi_protocol *multi_io; /**< State specific to OpenVPN using TCP
+ * as external transport. */
struct ifconfig_pool *ifconfig_pool;
struct frequency_limit *new_connection_limiter;
struct initial_packet_rate_limit *initial_rate_limiter;
new file mode 100644
@@ -0,0 +1,619 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2002-2023 OpenVPN Inc <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "syshead.h"
+
+#include "memdbg.h"
+
+#include "multi.h"
+#include "forward.h"
+#include "multi_io.h"
+
+#ifdef HAVE_SYS_INOTIFY_H
+#include <sys/inotify.h>
+#endif
+
+/*
+ * Special tags passed to event.[ch] functions
+ */
+#define MULTI_IO_SOCKET ((void *)1)
+#define MULTI_IO_TUN ((void *)2)
+#define MULTI_IO_SIG ((void *)3) /* Only on Windows */
+#define MULTI_IO_MANAGEMENT ((void *)4)
+#define MULTI_IO_FILE_CLOSE_WRITE ((void *)5)
+#define MULTI_IO_DCO ((void *)6)
+
+struct ta_iow_flags
+{
+ unsigned int flags;
+ unsigned int ret;
+ unsigned int tun;
+ unsigned int sock;
+};
+
+#ifdef ENABLE_DEBUG
+static const char *
+pract(int action)
+{
+ switch (action)
+ {
+ 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 "?";
+ }
+}
+#endif /* ENABLE_DEBUG */
+
+struct multi_protocol *
+multi_protocol_init(int maxevents, int *maxclients)
+{
+ struct multi_protocol *multi_io;
+ const int extra_events = BASE_N_EVENTS;
+
+ ASSERT(maxevents >= 1);
+ ASSERT(maxclients);
+
+ ALLOC_OBJ_CLEAR(multi_io, struct multi_protocol);
+ multi_io->maxevents = maxevents + extra_events;
+ multi_io->es = event_set_init(&multi_io->maxevents, 0);
+ wait_signal(multi_io->es, MULTI_IO_SIG);
+ ALLOC_ARRAY(multi_io->esr, struct event_set_return, multi_io->maxevents);
+ *maxclients = max_int(min_int(multi_io->maxevents - extra_events, *maxclients), 1);
+ msg(D_MULTI_LOW, "MULTI: TCP INIT maxclients=%d maxevents=%d", *maxclients, multi_io->maxevents);
+ return multi_io;
+}
+
+void
+multi_protocol_free(struct multi_protocol *multi_io)
+{
+ if (multi_io)
+ {
+ event_free(multi_io->es);
+ free(multi_io->esr);
+ free(multi_io);
+ }
+}
+
+int
+multi_protocol_io_wait(struct multi_context *m)
+{
+ int status, i;
+ unsigned int *persistent = &m->multi_io->tun_rwflags;
+
+ for (i = 0; i < m->top.c1.link_sockets_num; i++)
+ {
+ socket_set_listen_persistent(m->top.c2.link_sockets[i], m->multi_io->es,
+ &m->top.c2.link_sockets[i]->ev_arg);
+ }
+
+#ifdef _WIN32
+ if (tuntap_is_wintun(m->top.c1.tuntap))
+ {
+ if (!tuntap_ring_empty(m->top.c1.tuntap))
+ {
+ /* there is data in wintun ring buffer, read it immediately */
+ m->multi_io->esr[0].arg = MULTI_IO_TUN;
+ m->multi_io->esr[0].rwflags = EVENT_READ;
+ m->multi_io->n_esr = 1;
+ return 1;
+ }
+ persistent = NULL;
+ }
+#endif
+ tun_set(m->top.c1.tuntap, m->multi_io->es, EVENT_READ, MULTI_IO_TUN, persistent);
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
+ dco_event_set(&m->top.c1.tuntap->dco, m->multi_io->es, MULTI_IO_DCO);
+#endif
+
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ management_socket_set(management, m->multi_io->es, MULTI_IO_MANAGEMENT, &m->multi_io->management_persist_flags);
+ }
+#endif
+
+#ifdef ENABLE_ASYNC_PUSH
+ /* arm inotify watcher */
+ event_ctl(m->multi_io->es, m->top.c2.inotify_fd, EVENT_READ, MULTI_IO_FILE_CLOSE_WRITE);
+#endif
+
+ status = event_wait(m->multi_io->es, &m->top.c2.timeval, m->multi_io->esr, m->multi_io->maxevents);
+ update_time();
+ m->multi_io->n_esr = 0;
+ if (status > 0)
+ {
+ m->multi_io->n_esr = status;
+ }
+ return status;
+}
+
+static int
+multi_protocol_wait_lite(struct multi_context *m, struct multi_instance *mi, const int action, bool *tun_input_pending)
+{
+ struct context *c = multi_tcp_context(m, mi);
+ unsigned int looking_for = 0;
+
+ dmsg(D_MULTI_DEBUG, "MULTI PROTOCOL: multi_protocol_wait_lite a=%s mi=" ptr_format,
+ pract(action),
+ (ptr_type)mi);
+
+ tv_clear(&c->c2.timeval); /* ZERO-TIMEOUT */
+
+ switch (action)
+ {
+ 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 PROTOCOL: multi_protocol_wait_lite, unhandled action=%d", action);
+ }
+
+ if (tun_input_pending && (c->c2.event_set_status & TUN_READ))
+ {
+ *tun_input_pending = true;
+ }
+
+ if (c->c2.event_set_status & looking_for)
+ {
+ return action;
+ }
+ else
+ {
+ switch (action)
+ {
+ /* MULTI PROTOCOL 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;
+ }
+}
+
+static struct multi_instance *
+multi_protocol_dispatch(struct multi_context *m, struct multi_instance *mi, const int action)
+{
+ const unsigned int mpp_flags = MPP_PRE_SELECT|MPP_RECORD_TOUCH;
+ struct multi_instance *touched = mi;
+ m->mpp_touched = &touched;
+
+ dmsg(D_MULTI_DEBUG, "MULTI PROTOCOL: multi_protocol_dispatch a=%s mi=" ptr_format,
+ pract(action),
+ (ptr_type)mi);
+
+ switch (action)
+ {
+ 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_sockets);
+ ASSERT(mi->context.c2.link_sockets[0]);
+ set_prefix(mi);
+ read_incoming_link(&mi->context, mi->context.c2.link_sockets[0]);
+ clear_prefix();
+ if (!IS_SIG(&mi->context))
+ {
+ multi_process_incoming_link(m, mi, mpp_flags,
+ mi->context.c2.link_sockets[0]);
+ if (!IS_SIG(&mi->context))
+ {
+ stream_buf_read_setup(mi->context.c2.link_sockets[0]);
+ }
+ }
+ 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 PROTOCOL: multi_protocol_dispatch, unhandled action=%d", action);
+ }
+
+ m->mpp_touched = NULL;
+ return touched;
+}
+
+static int
+multi_protocol_post(struct multi_context *m, struct multi_instance *mi, const int action)
+{
+ struct context *c = multi_tcp_context(m, mi);
+ int newaction = TA_UNDEF;
+
+#define MTP_NONE 0
+#define MTP_TUN_OUT (1<<0)
+#define MTP_LINK_OUT (1<<1)
+ unsigned int flags = MTP_NONE;
+
+ if (TUN_OUT(c))
+ {
+ flags |= MTP_TUN_OUT;
+ }
+ if (LINK_OUT(c))
+ {
+ flags |= MTP_LINK_OUT;
+ }
+
+ switch (flags)
+ {
+ 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 && sockets_read_residual(c))
+ {
+ 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 PROTOCOL: multi_protocol_post bad state, mi=%s flags=%d",
+ multi_instance_string(mi, false, &gc),
+ flags);
+ gc_free(&gc);
+ break;
+ }
+ }
+
+ dmsg(D_MULTI_DEBUG, "MULTI PROTOCOL: multi_protocol_post %s -> %s",
+ pract(action),
+ pract(newaction));
+
+ return newaction;
+}
+
+void
+multi_protocol_process_io(struct multi_context *m)
+{
+ struct multi_protocol *multi_io = m->multi_io;
+ int i;
+
+ for (i = 0; i < multi_io->n_esr; ++i)
+ {
+ struct event_set_return *e = &multi_io->esr[i];
+ struct event_arg *ev_arg = (struct event_arg *)e->arg;
+
+ /* incoming data for instance or listening socket? */
+ if (e->arg >= MULTI_N)
+ {
+ switch (ev_arg->type)
+ {
+ struct multi_instance *mi;
+
+ /* react to event on child instance */
+ case EVENT_ARG_MULTI_INSTANCE:
+ if (!ev_arg->u.mi)
+ {
+ msg(D_MULTI_ERRORS, "MULTI PROTOCOL: multi_proto_proc_io: null minstance");
+ break;
+ }
+
+ mi = ev_arg->u.mi;
+ if (e->rwflags & EVENT_WRITE)
+ {
+ multi_protocol_action(m, mi, TA_SOCKET_WRITE_READY, false);
+ }
+ else if (e->rwflags & EVENT_READ)
+ {
+ multi_protocol_action(m, mi, TA_SOCKET_READ, false);
+ }
+ break;
+
+ /* new incoming TCP client attempting to connect? */
+ case EVENT_ARG_LINK_SOCKET:
+ if (!ev_arg->u.ls)
+ {
+ msg(D_MULTI_ERRORS, "MULTI PROTOCOL: multi_proto_proc_io: null socket");
+ break;
+ }
+
+ if (!proto_is_dgram(ev_arg->u.ls->info.proto))
+ {
+ socket_reset_listen_persistent(ev_arg->u.ls);
+ mi = multi_create_instance_tcp(m, ev_arg->u.ls);
+ if (mi)
+ {
+ multi_protocol_action(m, mi, TA_INITIAL, false);
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+#ifdef ENABLE_MANAGEMENT
+ if (e->arg == MULTI_IO_MANAGEMENT)
+ {
+ ASSERT(management);
+ management_io(management);
+ }
+ else
+#endif
+ /* incoming data on TUN? */
+ if (e->arg == MULTI_IO_TUN)
+ {
+ if (e->rwflags & EVENT_WRITE)
+ {
+ multi_protocol_action(m, NULL, TA_TUN_WRITE, false);
+ }
+ else if (e->rwflags & EVENT_READ)
+ {
+ multi_protocol_action(m, NULL, TA_TUN_READ, false);
+ }
+ }
+ /* new incoming TCP client attempting to connect? */
+ else if (e->arg == MULTI_IO_SOCKET)
+ {
+ struct multi_instance *mi;
+ ASSERT(m->top.c2.link_sockets[0]);
+ socket_reset_listen_persistent(m->top.c2.link_sockets[0]);
+ mi = multi_create_instance_tcp(m, m->top.c2.link_sockets[0]);
+ if (mi)
+ {
+ multi_protocol_action(m, mi, TA_INITIAL, false);
+ }
+ }
+#if defined(ENABLE_DCO) && (defined(TARGET_LINUX) || defined(TARGET_FREEBSD))
+ /* incoming data on DCO? */
+ else if (e->arg == MULTI_IO_DCO)
+ {
+ multi_process_incoming_dco(m);
+ }
+#endif
+ /* signal received? */
+ else if (e->arg == MULTI_IO_SIG)
+ {
+ get_signal(&m->top.sig->signal_received);
+ }
+#ifdef ENABLE_ASYNC_PUSH
+ else if (e->arg == MULTI_IO_FILE_CLOSE_WRITE)
+ {
+ multi_process_file_closed(m, MPP_PRE_SELECT | MPP_RECORD_TOUCH);
+ }
+#endif
+ }
+ if (IS_SIG(&m->top))
+ {
+ break;
+ }
+ }
+ multi_io->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_protocol_action(m, mi, TA_SOCKET_WRITE, true);
+ }
+ }
+}
+
+void
+multi_protocol_action(struct multi_context *m, struct multi_instance *mi, int action, bool poll)
+{
+ bool tun_input_pending = false;
+
+ do
+ {
+ dmsg(D_MULTI_DEBUG, "MULTI PROTOCOL: multi_protocol_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 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_protocol_wait_lite(m, mi, action, &tun_input_pending);
+ if (action == TA_UNDEF)
+ {
+ msg(M_FATAL, "MULTI PROTOCOL: I/O wait required blocking in multi_protocol_action, action=%d", orig_action);
+ }
+ }
+
+ /*
+ * Dispatch the action
+ */
+ struct multi_instance *touched = multi_protocol_dispatch(m, mi, action);
+
+ /*
+ * Signal received or 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_protocol_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);
+}
+
+void
+multi_protocol_delete_event(struct multi_protocol *multi_io, event_t event)
+{
+ if (multi_io && multi_io->es)
+ {
+ event_del(multi_io->es, event);
+ }
+}
new file mode 100644
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2002-2023 OpenVPN Inc <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Multi-protocol specific code for --mode server
+ */
+
+#ifndef MULTI_IO_H
+#define MULTI_IO_H
+
+#include "event.h"
+
+/*
+ * I/O processing 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
+
+/*
+ * Extra state info needed for Multi-protocol
+ */
+struct multi_protocol
+{
+ struct event_set *es;
+ struct event_set_return *esr;
+ int n_esr;
+ int maxevents;
+ unsigned int tun_rwflags;
+ unsigned int udp_flags;
+#ifdef ENABLE_MANAGEMENT
+ unsigned int management_persist_flags;
+#endif
+};
+
+struct multi_protocol *multi_protocol_init(int maxevents, int *maxclients);
+
+void multi_protocol_free(struct multi_protocol *multi_io);
+
+int multi_protocol_io_wait(struct multi_context *m);
+
+void multi_protocol_process_io(struct multi_context *m);
+
+void multi_protocol_action(struct multi_context *m, struct multi_instance *mi, int action, bool poll);
+
+void multi_protocol_delete_event(struct multi_protocol *multi_io, event_t event);
+
+#endif /* ifndef MULTI_IO_H */
@@ -1123,7 +1123,9 @@
/* force binding IPv6-only if an address was specified
* an it is a IPv6 */
if (sock->local_host && ai_family == AF_INET6)
+ {
v6only = true;
+ }
socket_bind(sock->sd, sock->info.lsa->bind_local,
ai_family, "TCP/UDP", v6only);
Attention is currently required from: flichtenheld, plaisthos. Hello plaisthos, flichtenheld, I'd like you to do a code review. Please visit http://gerrit.openvpn.net/c/openvpn/+/763?usp=email to review the following change. Change subject: multiproto: move generic event handling code in dedicated files ...................................................................... multiproto: move generic event handling code in dedicated files Introduced multi_io.h and multi_io.c files to centralize all codes related to multiple protocols. Renamed the struct mtcp to struct multi_protocol since it encompasses the event_set used by the parent context in server mode. Several methods have also been renamed and moved to fit the multiproto structure: - multi_tcp_init() -> multi_protocol_init(); - multi_tcp_free() -> multi_protocol_free(); - multi_tcp_wait() -> multi_protocol_io_wait(); and so forth. Change-Id: I1e5a84969988e4f027a18658d4ab268c13fbf929 Signed-off-by: Gianmarco De Gregori <gianmarco@mandelbit.com> --- M CMakeLists.txt M src/openvpn/Makefile.am M src/openvpn/forward.h M src/openvpn/mtcp.c M src/openvpn/mtcp.h M src/openvpn/multi.c M src/openvpn/multi.h A src/openvpn/multi_io.c A src/openvpn/multi_io.h M src/openvpn/socket.c 10 files changed, 740 insertions(+), 621 deletions(-) git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/63/763/1