From 20c8675ba46bda97330a4117c459a59a9f1c465e Mon Sep 17 00:00:00 2001 From: Alberto Gonzalez Iniesta Date: Mon, 21 Nov 2016 09:37:33 +0100 Subject: New upstream version 2.4~beta1 --- src/openvpn/multi.c | 410 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 359 insertions(+), 51 deletions(-) (limited to 'src/openvpn/multi.c') diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index 577c5d3..8f3d34e 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 +#define INOTIFY_EVENT_BUFFER_SIZE 16384 +#endif + #include "syshead.h" #if P2MP_SERVER @@ -38,6 +43,8 @@ #include "otime.h" #include "gremlin.h" #include "mstats.h" +#include "ssl_verify.h" +#include #include "memdbg.h" @@ -119,10 +126,8 @@ learn_address_script (const struct multi_context *m, { struct argv argv = argv_new (); setenv_str (es, "script_type", "learn-address"); - argv_printf (&argv, "%sc %s %s", - m->top.options.learn_address_script, - op, - mroute_addr_print (addr, &gc)); + argv_parse_cmd (&argv, m->top.options.learn_address_script); + argv_printf_cat (&argv, "%s %s", op, mroute_addr_print (addr, &gc)); if (mi) argv_printf_cat (&argv, "%s", tls_common_name (mi->context.c2.tls_multi, false)); if (!openvpn_run_script (&argv, es, 0, "--learn-address")) @@ -241,6 +246,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. */ @@ -302,6 +324,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. @@ -372,6 +405,8 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa */ m->max_clients = t->options.max_clients; + m->instances = calloc(m->max_clients, sizeof(struct multi_instance*)); + /* * Initialize multi-socket TCP I/O wait object */ @@ -392,6 +427,8 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa t->options.stale_routes_check_interval, t->options.stale_routes_ageing_time); event_timeout_init (&m->stale_routes_check_et, t->options.stale_routes_check_interval, 0); } + + m->deferred_shutdown_signal.signal_received = 0; } const char * @@ -399,7 +436,7 @@ multi_instance_string (const struct multi_instance *mi, bool null, struct gc_are { if (mi) { - struct buffer out = alloc_buf_gc (256, gc); + struct buffer out = alloc_buf_gc (MULTI_PREFIX_MAX_LENGTH, gc); const char *cn = tls_common_name (mi->context.c2.tls_multi, true); if (cn) @@ -416,21 +453,27 @@ multi_instance_string (const struct multi_instance *mi, bool null, struct gc_are void generate_prefix (struct multi_instance *mi) { - mi->msg_prefix = multi_instance_string (mi, true, &mi->gc); + struct gc_arena gc = gc_new(); + const char *prefix = multi_instance_string (mi, true, &gc); + if (prefix) + strncpynt(mi->msg_prefix, prefix, sizeof(mi->msg_prefix)); + else + mi->msg_prefix[0] = '\0'; set_prefix (mi); + gc_free(&gc); } void ungenerate_prefix (struct multi_instance *mi) { - mi->msg_prefix = NULL; + mi->msg_prefix[0] = '\0'; set_prefix (mi); } static const char * mi_prefix (const struct multi_instance *mi) { - if (mi && mi->msg_prefix) + if (mi && mi->msg_prefix[0]) return mi->msg_prefix; else return "UNDEF_I"; @@ -450,10 +493,10 @@ multi_del_iroutes (struct multi_context *m, 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); + mroute_helper_del_iroute46 (m->route_helper, ir->netbits); for ( ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next ) - mroute_helper_del_iroute6 (m->route_helper, ir6); + mroute_helper_del_iroute46 (m->route_helper, ir6->netbits); } } @@ -500,7 +543,7 @@ multi_client_disconnect_script (struct multi_context *m, { struct argv argv = argv_new (); setenv_str (mi->context.c2.es, "script_type", "client-disconnect"); - argv_printf (&argv, "%sc", mi->context.options.client_disconnect_script); + argv_parse_cmd (&argv, mi->context.options.client_disconnect_script); openvpn_run_script (&argv, mi->context.c2.es, 0, "--client-disconnect"); argv_reset (&argv); } @@ -552,6 +595,17 @@ 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 + + if (mi->context.c2.tls_multi->peer_id != MAX_PEER_ID) + m->instances[mi->context.c2.tls_multi->peer_id] = NULL; + schedule_remove_entry (m->schedule, (struct schedule_entry *) mi); ifconfig_pool_release (m->ifconfig_pool, mi->vaddr_handle, false); @@ -628,6 +682,13 @@ multi_uninit (struct multi_context *m) #endif m->hash = NULL; + 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); @@ -704,6 +765,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"); @@ -807,8 +873,8 @@ multi_print_status (struct multi_context *m, struct status_output *so, const int */ status_printf (so, "TITLE%c%s", sep, title_string); status_printf (so, "TIME%c%s%c%u", sep, time_string (now, 0, false, &gc_top), sep, (unsigned int)now); - status_printf (so, "HEADER%cCLIENT_LIST%cCommon Name%cReal Address%cVirtual Address%cBytes Received%cBytes Sent%cConnected Since%cConnected Since (time_t)%cUsername", - sep, sep, sep, sep, sep, sep, sep, sep, sep); + status_printf (so, "HEADER%cCLIENT_LIST%cCommon Name%cReal Address%cVirtual Address%cVirtual IPv6 Address%cBytes Received%cBytes Sent%cConnected Since%cConnected Since (time_t)%cUsername%cClient ID%cPeer ID", + sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep); hash_iterator_init (m->hash, &hi); while ((he = hash_iterator_next (&hi))) { @@ -817,15 +883,28 @@ multi_print_status (struct multi_context *m, struct status_output *so, const int if (!mi->halt) { - status_printf (so, "CLIENT_LIST%c%s%c%s%c%s%c" counter_format "%c" counter_format "%c%s%c%u%c%s", + status_printf (so, "CLIENT_LIST%c%s%c%s%c%s%c%s%c" counter_format "%c" counter_format "%c%s%c%u%c%s%c" +#ifdef MANAGEMENT_DEF_AUTH + "%lu" +#else + "" +#endif + "%c%"PRIu32, sep, tls_common_name (mi->context.c2.tls_multi, false), sep, mroute_addr_print (&mi->real, &gc), sep, print_in_addr_t (mi->reporting_addr, IA_EMPTY_IF_UNDEF, &gc), + sep, print_in6_addr (mi->reporting_addr_ipv6, IA_EMPTY_IF_UNDEF, &gc), sep, mi->context.c2.link_read_bytes, sep, mi->context.c2.link_write_bytes, sep, time_string (mi->created, 0, false, &gc), sep, (unsigned int)mi->created, - sep, tls_username (mi->context.c2.tls_multi, false)); + sep, tls_username (mi->context.c2.tls_multi, false), +#ifdef MANAGEMENT_DEF_AUTH + sep, mi->context.c2.mda_context.cid, +#else + sep, +#endif + sep, mi->context.c2.tls_multi ? mi->context.c2.tls_multi->peer_id : UINT32_MAX); } gc_free (&gc); } @@ -896,6 +975,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 } /* @@ -1113,7 +1199,7 @@ multi_learn_in6_addr (struct multi_context *m, addr.len = 16; addr.type = MR_ADDR_IPV6; addr.netbits = 0; - memcpy( &addr.addr, &a6, sizeof(a6) ); + addr.v6.addr = a6; if (netbits >= 0) { @@ -1158,23 +1244,18 @@ multi_add_iroutes (struct multi_context *m, print_in_addr_t (ir->network, 0, &gc), multi_instance_string (mi, false, &gc)); - mroute_helper_add_iroute (m->route_helper, ir); + mroute_helper_add_iroute46 (m->route_helper, ir->netbits); multi_learn_in_addr_t (m, mi, ir->network, ir->netbits, false); } for ( ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next ) { - if (ir6->netbits >= 0) - msg (D_MULTI_LOW, "MULTI: internal route %s/%d -> %s", + msg (D_MULTI_LOW, "MULTI: internal route %s/%d -> %s", print_in6_addr (ir6->network, 0, &gc), ir6->netbits, multi_instance_string (mi, false, &gc)); - else - msg (D_MULTI_LOW, "MULTI: internal route %s -> %s", - print_in6_addr (ir6->network, 0, &gc), - multi_instance_string (mi, false, &gc)); - mroute_helper_add_iroute6 (m->route_helper, ir6); + mroute_helper_add_iroute46 (m->route_helper, ir6->netbits); multi_learn_in6_addr (m, mi, ir6->network, ir6->netbits, false); } @@ -1289,16 +1370,13 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi) 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; -#ifdef ENABLE_CLIENT_NAT mi->context.c2.push_ifconfig_local_alias = mi->context.options.push_ifconfig_local_alias; -#endif /* the current implementation does not allow "static IPv4, pool IPv6", * (see below) so issue a warning if that happens - don't break the * session, though, as we don't even know if this client WANTS IPv6 */ - if ( mi->context.c1.tuntap->ipv6 && - mi->context.options.ifconfig_ipv6_pool_defined && + if ( mi->context.options.ifconfig_ipv6_pool_defined && ! mi->context.options.push_ifconfig_ipv6_defined ) { msg( M_INFO, "MULTI_sva: WARNING: if --ifconfig-push is used for IPv4, automatic IPv6 assignment from --ifconfig-ipv6-pool does not work. Use --ifconfig-ipv6-push for IPv6 then." ); @@ -1351,10 +1429,10 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi) if ( mi->context.options.ifconfig_ipv6_pool_defined ) { mi->context.c2.push_ifconfig_ipv6_local = remote_ipv6; - mi->context.c2.push_ifconfig_ipv6_remote = + mi->context.c2.push_ifconfig_ipv6_remote = mi->context.c1.tuntap->local_ipv6; - mi->context.c2.push_ifconfig_ipv6_netbits = - mi->context.options.ifconfig_ipv6_pool_netbits; + mi->context.c2.push_ifconfig_ipv6_netbits = + mi->context.options.ifconfig_ipv6_netbits; mi->context.c2.push_ifconfig_ipv6_defined = true; } } @@ -1371,8 +1449,7 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi) * way round ("dynamic IPv4, static IPv6") or "both static" makes sense * -> and so it's implemented right now */ - if ( mi->context.c1.tuntap->ipv6 && - mi->context.options.push_ifconfig_ipv6_defined ) + if ( mi->context.options.push_ifconfig_ipv6_defined ) { mi->context.c2.push_ifconfig_ipv6_local = mi->context.options.push_ifconfig_ipv6_local; @@ -1430,7 +1507,7 @@ multi_set_virtual_addr_env (struct multi_context *m, struct multi_instance *mi) setenv_del (mi->context.c2.es, "ifconfig_pool_remote_ip6"); setenv_del (mi->context.c2.es, "ifconfig_pool_ip6_netbits"); - if (mi->context.c1.tuntap->ipv6 && mi->context.c2.push_ifconfig_ipv6_defined) + if (mi->context.c2.push_ifconfig_ipv6_defined) { setenv_in6_addr (mi->context.c2.es, "ifconfig_pool_remote", @@ -1755,9 +1832,8 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi goto script_failed; } - argv_printf (&argv, "%sc %s", - mi->context.options.client_connect_script, - dc_file); + argv_parse_cmd (&argv, mi->context.options.client_connect_script); + argv_printf_cat (&argv, "%s", dc_file); if (openvpn_run_script (&argv, mi->context.c2.es, 0, "--client-connect")) { @@ -1868,9 +1944,18 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi /* set our client's VPN endpoint for status reporting purposes */ mi->reporting_addr = mi->context.c2.push_ifconfig_local; + mi->reporting_addr_ipv6 = mi->context.c2.push_ifconfig_ipv6_local; /* set context-level authentication flag */ mi->context.c2.context_auth = CAS_SUCCEEDED; + +#ifdef ENABLE_ASYNC_PUSH + /* authentication complete, send push reply */ + if (mi->context.c2.push_request_received) + { + process_incoming_push_request(&mi->context); + } +#endif } else { @@ -1900,6 +1985,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. @@ -2060,19 +2197,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); } } @@ -2107,6 +2275,72 @@ multi_process_post (struct multi_context *m, struct multi_instance *mi, const un return ret; } +void multi_process_float (struct multi_context* m, struct multi_instance* mi) +{ + struct mroute_addr real; + struct hash *hash = m->hash; + struct gc_arena gc = gc_new (); + + if (!mroute_extract_openvpn_sockaddr (&real, &m->top.c2.from.dest, true)) + goto done; + + const uint32_t hv = hash_value (hash, &real); + struct hash_bucket *bucket = hash_bucket (hash, hv); + + /* make sure that we don't float to an address taken by another client */ + struct hash_element *he = hash_lookup_fast (hash, bucket, &real, hv); + if (he) + { + struct multi_instance *ex_mi = (struct multi_instance *) he->value; + + struct tls_multi *m1 = mi->context.c2.tls_multi; + struct tls_multi *m2 = ex_mi->context.c2.tls_multi; + + /* do not float if target address is taken by client with another cert */ + if (!cert_hash_compare(m1->locked_cert_hash_set, m2->locked_cert_hash_set)) + { + msg (D_MULTI_LOW, "Disallow float to an address taken by another client %s", + multi_instance_string (ex_mi, false, &gc)); + + mi->context.c2.buf.len = 0; + + goto done; + } + + msg (D_MULTI_MEDIUM, "closing instance %s", multi_instance_string (ex_mi, false, &gc)); + multi_close_instance(m, ex_mi, false); + } + + msg (D_MULTI_MEDIUM, "peer %" PRIu32 " (%s) floated from %s to %s", + mi->context.c2.tls_multi->peer_id, + tls_common_name (mi->context.c2.tls_multi, false), + mroute_addr_print (&mi->real, &gc), + print_link_socket_actual (&m->top.c2.from, &gc)); + + /* change external network address of the remote peer */ + mi->real = real; + generate_prefix (mi); + + mi->context.c2.from = m->top.c2.from; + mi->context.c2.to_link_addr = &mi->context.c2.from; + + /* inherit parent link_socket and link_socket_info */ + mi->context.c2.link_socket = m->top.c2.link_socket; + mi->context.c2.link_socket_info->lsa->actual = m->top.c2.from; + + tls_update_remote_addr (mi->context.c2.tls_multi, &mi->context.c2.from); + + ASSERT (hash_add (m->hash, &mi->real, mi, true)); + ASSERT (hash_add (m->iter, &mi->real, mi, true)); + +#ifdef MANAGEMENT_DEF_AUTH + ASSERT (hash_add (m->cid_hash, &mi->context.c2.mda_context.cid, mi, true)); +#endif + +done: + gc_free (&gc); +} + /* * Process packets in the TCP/UDP socket -> TUN/TAP interface direction, * i.e. client -> server direction. @@ -2121,6 +2355,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins unsigned int mroute_flags; struct multi_instance *mi; bool ret = true; + bool floated = false; if (m->pending) return true; @@ -2130,7 +2365,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins #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)); + multi_set_pending (m, multi_get_create_instance_udp (m, &floated)); } else multi_set_pending (m, instance); @@ -2148,13 +2383,30 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins 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 (!floated) + c->c2.from = m->top.c2.from; } if (BLEN (&c->c2.buf) > 0) { + struct link_socket_info *lsi; + const uint8_t *orig_buf; + /* decrypt in instance context */ - process_incoming_link (c); + + perf_push (PERF_PROC_IN_LINK); + lsi = get_link_socket_info (c); + orig_buf = c->c2.buf.data; + if (process_incoming_link_part1(c, lsi, floated)) + { + if (floated) + { + multi_process_float (m, m->pending); + } + + process_incoming_link_part2(c, lsi, orig_buf); + } + perf_pop (); if (TUNNEL_TYPE (m->top.c1.tuntap) == DEV_TYPE_TUN) { @@ -2176,7 +2428,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins { /* IPv6 link-local address (fe80::xxx)? */ if ( (src.type & MR_ADDR_MASK) == MR_ADDR_IPV6 && - src.addr[0] == 0xfe && src.addr[1] == 0x80 ) + IN6_IS_ADDR_LINKLOCAL (&src.v6.addr) ) { /* do nothing, for now. TODO: add address learning */ } @@ -2478,10 +2730,18 @@ multi_process_timeout (struct multi_context *m, const unsigned int mpp_flags) /* instance marked for wakeup? */ if (m->earliest_wakeup) { - set_prefix (m->earliest_wakeup); - ret = multi_process_post (m, m->earliest_wakeup, mpp_flags); + if (m->earliest_wakeup == (struct multi_instance*)&m->deferred_shutdown_signal) + { + schedule_remove_entry(m->schedule, (struct schedule_entry*) &m->deferred_shutdown_signal); + throw_signal(m->deferred_shutdown_signal.signal_received); + } + else + { + set_prefix (m->earliest_wakeup); + ret = multi_process_post (m, m->earliest_wakeup, mpp_flags); + clear_prefix (); + } m->earliest_wakeup = NULL; - clear_prefix (); } return ret; } @@ -2591,12 +2851,10 @@ multi_process_per_second_timers_dowork (struct multi_context *m) } void -multi_top_init (struct multi_context *m, const struct context *top, const bool alloc_buffers) +multi_top_init (struct multi_context *m, const struct context *top) { inherit_context_top (&m->top, top); - m->top.c2.buffers = NULL; - if (alloc_buffers) - m->top.c2.buffers = init_context_buffers (&top->c2.frame); + m->top.c2.buffers = init_context_buffers (&top->c2.frame); } void @@ -2606,6 +2864,48 @@ multi_top_free (struct multi_context *m) free_context_buffers (m->top.c2.buffers); } +static bool +is_exit_restart(int sig) +{ + return (sig == SIGUSR1 || sig == SIGTERM || sig == SIGHUP || sig == SIGINT); +} + +static void +multi_push_restart_schedule_exit(struct multi_context *m, bool next_server) +{ + struct hash_iterator hi; + struct hash_element *he; + struct timeval tv; + + /* tell all clients to restart */ + hash_iterator_init (m->iter, &hi); + while ((he = hash_iterator_next (&hi))) + { + struct multi_instance *mi = (struct multi_instance *) he->value; + if (!mi->halt) + { + send_control_channel_string (&mi->context, next_server ? "RESTART,[N]" : "RESTART", D_PUSH); + multi_schedule_context_wakeup(m, mi); + } + } + hash_iterator_free (&hi); + + /* reschedule signal */ + ASSERT (!openvpn_gettimeofday (&m->deferred_shutdown_signal.wakeup, NULL)); + tv.tv_sec = 2; + tv.tv_usec = 0; + tv_add (&m->deferred_shutdown_signal.wakeup, &tv); + + m->deferred_shutdown_signal.signal_received = m->top.sig->signal_received; + + schedule_add_entry (m->schedule, + (struct schedule_entry *) &m->deferred_shutdown_signal, + &m->deferred_shutdown_signal.wakeup, + compute_wakeup_sigma (&m->deferred_shutdown_signal.wakeup)); + + m->top.sig->signal_received = 0; +} + /* * Return true if event loop should break, * false if it should continue. @@ -2621,6 +2921,14 @@ multi_process_signal (struct multi_context *m) m->top.sig->signal_received = 0; return false; } + else if (proto_is_dgram(m->top.options.ce.proto) && + is_exit_restart(m->top.sig->signal_received) && + (m->deferred_shutdown_signal.signal_received == 0) && + m->top.options.ce.explicit_exit_notification != 0) + { + multi_push_restart_schedule_exit(m, m->top.options.ce.explicit_exit_notification == 2); + return false; + } return true; } -- cgit v1.2.3