When server exits / restarts (gets SIGUSR1, SIGTERM, SIGHUP, SIGINT) and explicit-exit-notify is set, server broadcasts new OCC_SERVER_EXIT command to all clients and reschedules received signal in 2 secs.
When client receives OCC_SERVER_EXIT, it fires SIGUSR1 and switches to the next server. Next server is defined as same remote with different IP address if remote resolves into multiple addresses or next remote otherwise. v2: - take into use explicit-exit-notify on the server side - OCC_SHUTTING_DOWN renamed to OCC_SERVER_EXIT - minor code prettifying --- src/openvpn/multi.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++---- src/openvpn/multi.h | 14 ++++++++++- src/openvpn/occ.c | 8 +++++++ src/openvpn/occ.h | 6 +++++ src/openvpn/options.c | 7 ++---- src/openvpn/options.h | 3 ++- 6 files changed, 93 insertions(+), 11 deletions(-) diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index 4412491..b0d0e08 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_shutdown_signal.signal_received = 0; } const char * @@ -603,6 +605,25 @@ multi_close_instance (struct multi_context *m, perf_pop (); } +void +multi_broadcast_server_exit (struct multi_context *m) +{ + msg (D_LOW, "multi_broadcast_server_exit"); + + 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_SERVER_EXIT); + + 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_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; } @@ -2699,6 +2728,12 @@ multi_top_free (struct multi_context *m) free_context_buffers (m->top.c2.buffers); } +bool +is_exit_restart(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 +2749,29 @@ 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) + { + /* broadcast OCC_SERVER_EXIT to clients */ + multi_broadcast_server_exit(m); + + /* schedule signal */ + openvpn_gettimeofday (&m->deferred_shutdown_signal.wakeup, NULL); + struct timeval tv; + 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 false; + } return true; } diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h index 32b89d2..d968626 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_shutdown_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..8302488 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_SERVER_EXIT: + msg (D_LOW, "RECEIVED OCC_SERVER_EXIT"); + c->sig->signal_received = SIGUSR1; + c->sig->signal_text = "remote-server-exit"; + 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..22ba4dc 100644 --- a/src/openvpn/occ.h +++ b/src/openvpn/occ.h @@ -69,6 +69,12 @@ */ #define OCC_EXIT 6 + +/* + * Notifies clients that server is exiting/restarting + */ +#define OCC_SERVER_EXIT 7 + /* * Used to conduct a load test command sequence * of UDP connection for empirical MTU measurement. diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 4ea03d1..c00b5a9 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -496,7 +496,8 @@ static const char usage_message[] = #endif #ifdef ENABLE_OCC "--explicit-exit-notify [n] : On exit/restart, send exit signal to\n" - " server/remote. n = # of retries, default=1.\n" + " server/remote or client. n = # of retries, default=1.\n" + " n is ignored in server mode.\n" #endif #ifdef ENABLE_CRYPTO "\n" @@ -2022,10 +2023,6 @@ options_postprocess_verify_ce (const struct options *options, const struct conne msg (M_USAGE, "--connect-freq only works with --mode server --proto udp. Try --max-clients instead."); if (!(dev == DEV_TYPE_TAP || (dev == DEV_TYPE_TUN && options->topology == TOP_SUBNET)) && options->ifconfig_pool_netmask) msg (M_USAGE, "The third parameter to --ifconfig-pool (netmask) is only valid in --dev tap mode"); -#ifdef ENABLE_OCC - if (ce->explicit_exit_notification) - msg (M_USAGE, "--explicit-exit-notify cannot be used with --mode server"); -#endif if (options->routes && (options->routes->flags & RG_ENABLE)) msg (M_USAGE, "--redirect-gateway cannot be used with --mode server (however --push \"redirect-gateway\" is fine)"); if (options->route_delay_defined) diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 7a8b21e..d232b83 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -121,7 +121,8 @@ struct connection_entry bool mssfix_default; /* true if --mssfix was supplied without a parameter */ #ifdef ENABLE_OCC - int explicit_exit_notification; /* Explicitly tell peer when we are exiting via OCC_EXIT message */ + /* Explicitly tell peer when we are exiting via OCC_EXIT (client to server) or OCC_SERVER_EXIT (server to client) message */ + int explicit_exit_notification; #endif # define CE_DISABLED (1<<0) -- 1.9.1