When server gets shutdown signal (SIGUSR1, SIGTERM, SIGHUP, SIGINT), it broadcasts new OCC_SHUTTING_DOWN command to all clients and reschedules received signal in 2 secs.
When client receives OCC_SHUTTING_DOWN, it fires SIGUSR1 and switches to the next remote. --- src/openvpn/multi.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++---- src/openvpn/multi.h | 14 +++++++++++- src/openvpn/occ.c | 8 +++++++ src/openvpn/occ.h | 6 +++++ 4 files changed, 86 insertions(+), 5 deletions(-) diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index 4412491..b5f2dd2 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -396,6 +396,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_signal.signal_received = 0; } const char * @@ -603,6 +605,25 @@ multi_close_instance (struct multi_context *m, perf_pop (); } +void +multi_broadcast_shutdown (struct multi_context *m) +{ + msg (D_LOW, "multi_broadcast_shutdown"); + + struct gc_arena gc = gc_new (); + struct buffer buf = alloc_buf_gc (BUF_SIZE (&m->top.c2.frame), &gc); + + buf_init (&buf, FRAME_HEADROOM (&m->top.c2.frame)); + buf_safe (&buf, MAX_RW_SIZE_TUN (&m->top.c2.frame)); + buf_write (&buf, occ_magic, OCC_STRING_SIZE); + + buf_write_u8 (&buf, OCC_SHUTTING_DOWN); + + multi_bcast (m, &buf, NULL, NULL); + + gc_free (&gc); +} + /* * Called on shutdown or restart. */ @@ -1952,7 +1973,7 @@ multi_unicast (struct multi_context *m, /* * Broadcast a packet to all clients. */ -static void +void multi_bcast (struct multi_context *m, const struct buffer *buf, const struct multi_instance *sender_instance, @@ -2571,10 +2592,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_signal) + { + schedule_remove_entry(m->schedule, (struct schedule_entry*) &m->deferred_signal); + throw_signal(m->deferred_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; } @@ -2699,6 +2728,10 @@ multi_top_free (struct multi_context *m) free_context_buffers (m->top.c2.buffers); } +bool is_shutdown_signal(int sig) { + return (sig == SIGUSR1 || sig == SIGTERM || sig == SIGHUP || sig == SIGINT); +} + /* * Return true if event loop should break, * false if it should continue. @@ -2714,6 +2747,28 @@ 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_shutdown_signal(m->top.sig->signal_received) && + (m->deferred_signal.signal_received == 0)) + { + // broadcast OCC_SHUTTING_DOWN to all connected clients + multi_broadcast_shutdown(m); + + // schedule signal + openvpn_gettimeofday (&m->deferred_signal.wakeup, NULL); + struct timeval tv; + tv.tv_sec = 2; + tv.tv_usec = 0; + tv_add (&m->deferred_signal.wakeup, &tv); + + m->deferred_signal.signal_received = m->top.sig->signal_received; + + schedule_add_entry (m->schedule, (struct schedule_entry *) &m->deferred_signal, + &m->deferred_signal.wakeup, compute_wakeup_sigma (&m->deferred_signal.wakeup)); + + m->top.sig->signal_received = 0; + return false; + } return true; } diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h index 32b89d2..c9c1940 100644 --- a/src/openvpn/multi.h +++ b/src/openvpn/multi.h @@ -57,6 +57,13 @@ struct multi_reap }; +struct deferred_signal_schedule_entry +{ + struct schedule_entry se; + int signal_received; + struct timeval wakeup; +}; + /** * Server-mode state structure for one single VPN tunnel. * @@ -172,6 +179,8 @@ struct multi_context { * Timer object for stale route check */ struct event_timeout stale_routes_check_et; + + struct deferred_signal_schedule_entry deferred_signal; }; /* @@ -190,7 +199,6 @@ struct multi_route time_t last_reference; }; - /**************************************************************************/ /** * Main event loop for OpenVPN in server mode. @@ -290,6 +298,10 @@ bool multi_process_post (struct multi_context *m, struct multi_instance *mi, con bool multi_process_incoming_link (struct multi_context *m, struct multi_instance *instance, const unsigned int mpp_flags); +void multi_bcast (struct multi_context *m, const struct buffer *buf, + const struct multi_instance *sender_instance, + const struct mroute_addr *sender_addr); + /** * Determine the destination VPN tunnel of a packet received over the * virtual tun/tap network interface and then process it accordingly. diff --git a/src/openvpn/occ.c b/src/openvpn/occ.c index ff48706..3695b07 100644 --- a/src/openvpn/occ.c +++ b/src/openvpn/occ.c @@ -390,6 +390,14 @@ process_received_occ_msg (struct context *c) c->sig->signal_received = SIGTERM; c->sig->signal_text = "remote-exit"; break; + + case OCC_SHUTTING_DOWN: + msg (D_LOW, "RECEIVED OCC_SHUTTING_DOWN"); + c->sig->signal_received = SIGUSR1; + c->sig->signal_text = "remote-shutdown"; + c->options.no_advance = false; + break; + } c->c2.buf.len = 0; /* don't pass packet on */ } diff --git a/src/openvpn/occ.h b/src/openvpn/occ.h index 5d88cc9..0d10f2e 100644 --- a/src/openvpn/occ.h +++ b/src/openvpn/occ.h @@ -69,6 +69,12 @@ */ #define OCC_EXIT 6 + +/* + * Notifies clients that server is shutting down + */ +#define OCC_SHUTTING_DOWN 7 + /* * Used to conduct a load test command sequence * of UDP connection for empirical MTU measurement. -- 1.9.1