summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac15
-rw-r--r--src/openvpn/forward.c8
-rw-r--r--src/openvpn/mtcp.c28
-rw-r--r--src/openvpn/mudp.c27
-rw-r--r--src/openvpn/multi.c155
-rw-r--r--src/openvpn/multi.h21
-rw-r--r--src/openvpn/openvpn.h10
-rw-r--r--src/openvpn/push.c69
-rw-r--r--src/openvpn/push.h2
9 files changed, 304 insertions, 31 deletions
diff --git a/configure.ac b/configure.ac
index 77b4915..3424584 100644
--- a/configure.ac
+++ b/configure.ac
@@ -271,6 +271,13 @@ AC_ARG_ENABLE(
[enable_systemd="no"]
)
+AC_ARG_ENABLE(
+ [async-push],
+ [AS_HELP_STRING([--enable-async-push], [enable async-push support @<:@default=no@:>@])],
+ [enable_async_push="yes"],
+ [enable_async_push="no"]
+)
+
AC_ARG_WITH(
[special-build],
[AS_HELP_STRING([--with-special-build=STRING], [specify special build string])],
@@ -1155,6 +1162,14 @@ if test "${enable_plugin_auth_pam}" = "yes"; then
fi
fi
+if test "${enable_async_push}" = "yes"; then
+ AC_CHECK_HEADERS(
+ [sys/inotify.h],
+ AC_DEFINE([ENABLE_ASYNC_PUSH], [1], [Enable async push]),
+ AC_MSG_ERROR([inotify.h not found.])
+ )
+fi
+
CONFIGURE_DEFINES="`set | grep '^enable_.*=' ; set | grep '^with_.*='`"
AC_DEFINE_UNQUOTED([CONFIGURE_DEFINES], ["`echo ${CONFIGURE_DEFINES}`"], [Configuration settings])
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index c17be35..62eb6fc 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -1384,6 +1384,9 @@ io_wait_dowork (struct context *c, const unsigned int flags)
#ifdef ENABLE_MANAGEMENT
static int management_shift = 6; /* depends on MANAGEMENT_READ and MANAGEMENT_WRITE */
#endif
+#ifdef ENABLE_ASYNC_PUSH
+ static int file_shift = 8; /* listening inotify events */
+#endif
/*
* Decide what kind of events we want to wait for.
@@ -1478,6 +1481,11 @@ io_wait_dowork (struct context *c, const unsigned int flags)
management_socket_set (management, c->c2.event_set, (void*)&management_shift, NULL);
#endif
+#ifdef ENABLE_ASYNC_PUSH
+ /* arm inotify watcher */
+ event_ctl (c->c2.event_set, c->c2.inotify_fd, EVENT_READ, (void*)&file_shift);
+#endif
+
/*
* Possible scenarios:
* (1) tcp/udp port has data available to read
diff --git a/src/openvpn/mtcp.c b/src/openvpn/mtcp.c
index dc15f09..b27c5eb 100644
--- a/src/openvpn/mtcp.c
+++ b/src/openvpn/mtcp.c
@@ -62,6 +62,10 @@
# define MTCP_MANAGEMENT ((void*)4)
#endif
+#ifdef ENABLE_ASYNC_PUSH
+#define MTCP_FILE_CLOSE_WRITE ((void*)5)
+#endif
+
#define MTCP_N ((void*)16) /* upper bound on MTCP_x */
struct ta_iow_flags
@@ -245,6 +249,12 @@ multi_tcp_wait (const struct context *c,
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;
@@ -636,6 +646,12 @@ multi_tcp_process_io (struct multi_context *m)
{
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;
@@ -684,6 +700,14 @@ tunnel_server_tcp (struct context *top)
/* finished with initialization */
initialization_sequence_completed (top, ISC_SERVER); /* --mode server --proto tcp-server */
+#ifdef ENABLE_ASYNC_PUSH
+ multi.top.c2.inotify_fd = inotify_init();
+ if (multi.top.c2.inotify_fd < 0)
+ {
+ msg (D_MULTI_ERRORS, "MULTI: inotify_init error: %s", strerror(errno));
+ }
+#endif
+
/* per-packet event loop */
while (true)
{
@@ -712,6 +736,10 @@ tunnel_server_tcp (struct context *top)
perf_pop ();
}
+#ifdef ENABLE_ASYNC_PUSH
+ close(top->c2.inotify_fd);
+#endif
+
/* shut down management interface */
uninit_management_callback_multi (&multi);
diff --git a/src/openvpn/mudp.c b/src/openvpn/mudp.c
index 57118f8..3aed3a0 100644
--- a/src/openvpn/mudp.c
+++ b/src/openvpn/mudp.c
@@ -38,6 +38,10 @@
#include "memdbg.h"
+#ifdef HAVE_SYS_INOTIFY_H
+#include <sys/inotify.h>
+#endif
+
/*
* Get a client instance based on real address. If
* the instance doesn't exist, create it while
@@ -177,6 +181,10 @@ multi_process_io_udp (struct multi_context *m)
strcat (buf, "TR/");
else if (status & TUN_WRITE)
strcat (buf, "TW/");
+#ifdef ENABLE_ASYNC_PUSH
+ else if (status & FILE_CLOSED)
+ strcat (buf, "FC/");
+#endif
printf ("IO %s\n", buf);
#endif
@@ -214,6 +222,13 @@ multi_process_io_udp (struct multi_context *m)
if (!IS_SIG (&m->top))
multi_process_incoming_tun (m, mpp_flags);
}
+#ifdef ENABLE_ASYNC_PUSH
+ /* INOTIFY callback */
+ else if (status & FILE_CLOSED)
+ {
+ multi_process_file_closed(m, mpp_flags);
+ }
+#endif
}
/*
@@ -276,6 +291,14 @@ tunnel_server_udp_single_threaded (struct context *top)
/* finished with initialization */
initialization_sequence_completed (top, ISC_SERVER); /* --mode server --proto udp */
+#ifdef ENABLE_ASYNC_PUSH
+ multi.top.c2.inotify_fd = inotify_init();
+ if (multi.top.c2.inotify_fd < 0)
+ {
+ msg (D_MULTI_ERRORS, "MULTI: inotify_init error: %s", strerror(errno));
+ }
+#endif
+
/* per-packet event loop */
while (true)
{
@@ -304,6 +327,10 @@ tunnel_server_udp_single_threaded (struct context *top)
perf_pop ();
}
+#ifdef ENABLE_ASYNC_PUSH
+ close(top->c2.inotify_fd);
+#endif
+
/* shut down management interface */
uninit_management_callback_multi (&multi);
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 902c4dc..05c36db 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -28,6 +28,11 @@
#include "config-msvc.h"
#endif
+#ifdef HAVE_SYS_INOTIFY_H
+#include <sys/inotify.h>
+#define INOTIFY_EVENT_BUFFER_SIZE 16384
+#endif
+
#include "syshead.h"
#if P2MP_SERVER
@@ -243,6 +248,23 @@ cid_compare_function (const void *key1, const void *key2)
#endif
+#ifdef ENABLE_ASYNC_PUSH
+static uint32_t
+/*
+ * inotify watcher descriptors are used as hash value
+ */
+int_hash_function (const void *key, uint32_t iv)
+{
+ return (unsigned long)key;
+}
+
+static bool
+int_compare_function (const void *key1, const void *key2)
+{
+ return (unsigned long)key1 == (unsigned long)key2;
+}
+#endif
+
/*
* Main initialization function, init multi_context object.
*/
@@ -304,6 +326,17 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa
cid_compare_function);
#endif
+#ifdef ENABLE_ASYNC_PUSH
+ /*
+ * Mapping between inotify watch descriptors and
+ * multi_instances.
+ */
+ m->inotify_watchers = hash_init (t->options.real_hash_size,
+ get_random(),
+ int_hash_function,
+ int_compare_function);
+#endif
+
/*
* This is our scheduler, for time-based wakeup
* events.
@@ -562,6 +595,14 @@ multi_close_instance (struct multi_context *m,
}
#endif
+#ifdef ENABLE_ASYNC_PUSH
+ if (mi->inotify_watch != -1)
+ {
+ hash_remove(m->inotify_watchers, (void*) (unsigned long)mi->inotify_watch);
+ mi->inotify_watch = -1;
+ }
+#endif
+
m->instances[mi->context.c2.tls_multi->peer_id] = NULL;
schedule_remove_entry (m->schedule, (struct schedule_entry *) mi);
@@ -642,6 +683,11 @@ multi_uninit (struct multi_context *m)
free(m->instances);
+#ifdef ENABLE_ASYNC_PUSH
+ hash_free (m->inotify_watchers);
+ m->inotify_watchers = NULL;
+#endif
+
schedule_free (m->schedule);
mbuf_free (m->mbuf);
ifconfig_pool_free (m->ifconfig_pool);
@@ -718,6 +764,11 @@ multi_create_instance (struct multi_context *m, const struct mroute_addr *real)
mi->context.c2.push_reply_deferred = true;
+#ifdef ENABLE_ASYNC_PUSH
+ mi->context.c2.push_request_received = false;
+ mi->inotify_watch = -1;
+#endif
+
if (!multi_process_post (m, mi, MPP_PRE_SELECT))
{
msg (D_MULTI_ERRORS, "MULTI: signal occurred during client instance initialization");
@@ -923,6 +974,13 @@ multi_print_status (struct multi_context *m, struct status_output *so, const int
status_flush (so);
gc_free (&gc_top);
}
+
+#ifdef ENABLE_ASYNC_PUSH
+ if (m->inotify_watchers)
+ {
+ msg (D_MULTI_DEBUG, "inotify watchers count: %d\n", hash_n_elements(m->inotify_watchers));
+ }
+#endif
}
/*
@@ -1877,6 +1935,14 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
/* set context-level authentication flag */
mi->context.c2.context_auth = CAS_SUCCEEDED;
+
+#ifdef ENABLE_ASYNC_PUSH
+ /* authentication complete, send push reply */
+ if (mi->context.c2.push_request_received)
+ {
+ process_incoming_push_request(&mi->context);
+ }
+#endif
}
else
{
@@ -1906,6 +1972,58 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
mi->context.c2.push_reply_deferred = false;
}
+#ifdef ENABLE_ASYNC_PUSH
+/*
+ * Called when inotify event is fired, which happens when acf file is closed or deleted.
+ * Continues authentication and sends push_reply.
+ */
+void
+multi_process_file_closed (struct multi_context *m, const unsigned int mpp_flags)
+{
+ char buffer[INOTIFY_EVENT_BUFFER_SIZE];
+ size_t buffer_i = 0;
+ int r = read (m->top.c2.inotify_fd, buffer, INOTIFY_EVENT_BUFFER_SIZE);
+
+ while (buffer_i < r)
+ {
+ /* parse inotify events */
+ struct inotify_event *pevent = (struct inotify_event *) &buffer[buffer_i];
+ size_t event_size = sizeof (struct inotify_event) + pevent->len;
+ buffer_i += event_size;
+
+ msg(D_MULTI_DEBUG, "MULTI: modified fd %d, mask %d", pevent->wd, pevent->mask);
+
+ struct multi_instance* mi = hash_lookup(m->inotify_watchers, (void*) (unsigned long) pevent->wd);
+
+ if (pevent->mask & IN_CLOSE_WRITE)
+ {
+ if (mi)
+ {
+ /* continue authentication and send push_reply */
+ multi_process_post (m, mi, mpp_flags);
+ }
+ else
+ {
+ msg(D_MULTI_ERRORS, "MULTI: multi_instance not found!");
+ }
+ }
+ else if (pevent->mask & IN_IGNORED)
+ {
+ /* this event is _always_ fired when watch is removed or file is deleted */
+ if (mi)
+ {
+ hash_remove(m->inotify_watchers, (void*) (unsigned long) pevent->wd);
+ mi->inotify_watch = -1;
+ }
+ }
+ else
+ {
+ msg(D_MULTI_ERRORS, "MULTI: unknown mask %d", pevent->mask);
+ }
+ }
+}
+#endif
+
/*
* Add a mbuf buffer to a particular
* instance.
@@ -2066,19 +2184,50 @@ multi_process_post (struct multi_context *m, struct multi_instance *mi, const un
if (!IS_SIG (&mi->context) && ((flags & MPP_PRE_SELECT) || ((flags & MPP_CONDITIONAL_PRE_SELECT) && !ANY_OUT (&mi->context))))
{
+#if defined(ENABLE_ASYNC_PUSH) && defined(ENABLE_DEF_AUTH)
+ bool was_authenticated = false;
+ struct key_state *ks = NULL;
+ if (mi->context.c2.tls_multi)
+ {
+ ks = &mi->context.c2.tls_multi->session[TM_ACTIVE].key[KS_PRIMARY];
+ was_authenticated = ks->authenticated;
+ }
+#endif
+
/* figure timeouts and fetch possible outgoing
to_link packets (such as ping or TLS control) */
pre_select (&mi->context);
- if (!IS_SIG (&mi->context))
+#if defined(ENABLE_ASYNC_PUSH) && defined(ENABLE_DEF_AUTH)
+ if (ks && ks->auth_control_file && ks->auth_deferred && !was_authenticated)
{
- /* tell scheduler to wake us up at some point in the future */
- multi_schedule_context_wakeup(m, mi);
+ /* watch acf file */
+ long watch_descriptor = inotify_add_watch(m->top.c2.inotify_fd, ks->auth_control_file, IN_CLOSE_WRITE | IN_ONESHOT);
+ if (watch_descriptor >= 0)
+ {
+ if (mi->inotify_watch != -1)
+ {
+ hash_remove(m->inotify_watchers, (void*) (unsigned long)mi->inotify_watch);
+ }
+ hash_add (m->inotify_watchers, (const uintptr_t*)watch_descriptor, mi, true);
+ mi->inotify_watch = watch_descriptor;
+ }
+ else
+ {
+ msg(M_NONFATAL, "MULTI: inotify_add_watch error: %s", strerror(errno));
+ }
+ }
+#endif
+ if (!IS_SIG (&mi->context))
+ {
/* connection is "established" when SSL/TLS key negotiation succeeds
and (if specified) auth user/pass succeeds */
if (!mi->connection_established_flag && CONNECTION_ESTABLISHED (&mi->context))
multi_connection_established (m, mi);
+
+ /* tell scheduler to wake us up at some point in the future */
+ multi_schedule_context_wakeup(m, mi);
}
}
diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h
index 32b89d2..69ed85e 100644
--- a/src/openvpn/multi.h
+++ b/src/openvpn/multi.h
@@ -105,6 +105,10 @@ struct multi_instance {
struct context context; /**< The context structure storing state
* for this VPN tunnel. */
+
+#ifdef ENABLE_ASYNC_PUSH
+ int inotify_watch; /* watch descriptor for acf */
+#endif
};
@@ -172,6 +176,11 @@ struct multi_context {
* Timer object for stale route check
*/
struct event_timeout stale_routes_check_et;
+
+#ifdef ENABLE_ASYNC_PUSH
+ /* mapping between inotify watch descriptors and multi_instances */
+ struct hash *inotify_watchers;
+#endif
};
/*
@@ -327,6 +336,18 @@ void multi_close_instance_on_signal (struct multi_context *m, struct multi_insta
void init_management_callback_multi (struct multi_context *m);
void uninit_management_callback_multi (struct multi_context *m);
+
+#ifdef ENABLE_ASYNC_PUSH
+/**
+ * Called when inotify event is fired, which happens when acf file is closed or deleted.
+ * Continues authentication and sends push_repl
+ *
+ * @param m multi_context
+ * @param mpp_flags
+ */
+void multi_process_file_closed (struct multi_context *m, const unsigned int mpp_flags);
+#endif
+
/*
* Return true if our output queue is not full
*/
diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h
index ef22269..4fab00b 100644
--- a/src/openvpn/openvpn.h
+++ b/src/openvpn/openvpn.h
@@ -241,6 +241,9 @@ struct context_2
# define MANAGEMENT_READ (1<<6)
# define MANAGEMENT_WRITE (1<<7)
# endif
+#ifdef ENABLE_ASYNC_PUSH
+# define FILE_CLOSED (1<<8)
+#endif
unsigned int event_set_status;
@@ -436,6 +439,9 @@ struct context_2
#if P2MP_SERVER
/* --ifconfig endpoints to be pushed to client */
bool push_reply_deferred;
+#ifdef ENABLE_ASYNC_PUSH
+ bool push_request_received;
+#endif
bool push_ifconfig_defined;
time_t sent_push_reply_expiry;
in_addr_t push_ifconfig_local;
@@ -479,6 +485,10 @@ struct context_2
#ifdef MANAGEMENT_DEF_AUTH
struct man_def_auth_context mda_context;
#endif
+
+#ifdef ENABLE_ASYNC_PUSH
+ int inotify_fd; /* descriptor for monitoring file changes */
+#endif
};
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index b9d0c4c..704818d 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -411,6 +411,46 @@ push_reset (struct options *o)
#endif
int
+process_incoming_push_request (struct context *c)
+{
+ int ret = PUSH_MSG_ERROR;
+
+#ifdef ENABLE_ASYNC_PUSH
+ c->c2.push_request_received = true;
+#endif
+ if (tls_authentication_status (c->c2.tls_multi, 0) == TLS_AUTHENTICATION_FAILED || c->c2.context_auth == CAS_FAILED)
+ {
+ const char *client_reason = tls_client_reason (c->c2.tls_multi);
+ send_auth_failed (c, client_reason);
+ ret = PUSH_MSG_AUTH_FAILURE;
+ }
+ else if (!c->c2.push_reply_deferred && c->c2.context_auth == CAS_SUCCEEDED)
+ {
+ time_t now;
+
+ openvpn_time (&now);
+ if (c->c2.sent_push_reply_expiry > now)
+ {
+ ret = PUSH_MSG_ALREADY_REPLIED;
+ }
+ else
+ {
+ if (send_push_reply (c))
+ {
+ ret = PUSH_MSG_REQUEST;
+ c->c2.sent_push_reply_expiry = now + 30;
+ }
+ }
+ }
+ else
+ {
+ ret = PUSH_MSG_REQUEST_DEFERRED;
+ }
+
+ return ret;
+}
+
+int
process_incoming_push_msg (struct context *c,
const struct buffer *buffer,
bool honor_received_options,
@@ -423,34 +463,7 @@ process_incoming_push_msg (struct context *c,
#if P2MP_SERVER
if (buf_string_compare_advance (&buf, "PUSH_REQUEST"))
{
- if (tls_authentication_status (c->c2.tls_multi, 0) == TLS_AUTHENTICATION_FAILED || c->c2.context_auth == CAS_FAILED)
- {
- const char *client_reason = tls_client_reason (c->c2.tls_multi);
- send_auth_failed (c, client_reason);
- ret = PUSH_MSG_AUTH_FAILURE;
- }
- else if (!c->c2.push_reply_deferred && c->c2.context_auth == CAS_SUCCEEDED)
- {
- time_t now;
-
- openvpn_time(&now);
- if (c->c2.sent_push_reply_expiry > now)
- {
- ret = PUSH_MSG_ALREADY_REPLIED;
- }
- else
- {
- if (send_push_reply (c))
- {
- ret = PUSH_MSG_REQUEST;
- c->c2.sent_push_reply_expiry = now + 30;
- }
- }
- }
- else
- {
- ret = PUSH_MSG_REQUEST_DEFERRED;
- }
+ ret = process_incoming_push_request(c);
}
else
#endif
diff --git a/src/openvpn/push.h b/src/openvpn/push.h
index 8c3f157..5eca45f 100644
--- a/src/openvpn/push.h
+++ b/src/openvpn/push.h
@@ -40,6 +40,8 @@
void incoming_push_message (struct context *c,
const struct buffer *buffer);
+int process_incoming_push_request (struct context *c);
+
int process_incoming_push_msg (struct context *c,
const struct buffer *buffer,
bool honor_received_options,