When server exits / restarts (gets SIGUSR1, SIGTERM, SIGHUP, SIGINT) and
explicit-exit-notify is set, server sends RESTART control channel
command to all clients and reschedules received signal in 2 secs.

When client receives RESTART command, it either reconnects to the same
server or advances to the new one, depends on parameter comes with
RESTART command - behavior is controlled by explicit-exit-notify in the
server config.

v4:
- Rebase on top of master
- Remove #ifdef ENABLE_OCC around connection_entry->explicit_exit_notification
since it is also used outside of OCC context
- Update usage message

v3:
- Use control channel "RESTART" command instead of new OCC code to
notify clients
- Configure on the server side (by value of explicit-exit-notify) if
client should reconnect to the same server or advance to the next one
- Fix compilation when OCC is disabled (--enable-small)
- Update man page

v2:
- Take into use explicit-exit-notify on the server side
- OCC_SHUTTING_DOWN renamed to OCC_SERVER_EXIT
- Code prettifying

Signed-off-by: Lev Stipakov <lstipa...@gmail.com>
---
 doc/openvpn.8         | 15 ++++++++++--
 src/openvpn/multi.c   | 66 ++++++++++++++++++++++++++++++++++++++++++++++++---
 src/openvpn/multi.h   |  9 +++++++
 src/openvpn/options.c |  7 +++---
 src/openvpn/options.h |  4 +---
 src/openvpn/push.c    |  6 +++++
 6 files changed, 95 insertions(+), 12 deletions(-)

diff --git a/doc/openvpn.8 b/doc/openvpn.8
index 2978b7f..dfb63fc 100644
--- a/doc/openvpn.8
+++ b/doc/openvpn.8
@@ -3886,8 +3886,19 @@ option will tell the server to immediately close its 
client instance object
 rather than waiting for a timeout.  The
 .B n
 parameter (default=1) controls the maximum number of attempts that the client
-will try to resend the exit notification message.  OpenVPN will not send any 
exit
-notifications unless this option is enabled.
+will try to resend the exit notification message. 
+
+In UDP server mode, send RESTART control channel command to connected clients. 
The
+.B n
+parameter (default=1) controls client behavior. With
+.B n
+= 1 client will attempt to reconnect
+to the same server, with
+.B n
+= 2 client will advance to the next server.
+
+OpenVPN will not send any exit
+notifications unless this option is enabled. 
 .\"*********************************************************
 .SS Data Channel Encryption Options:
 These options are meaningful for both Static & TLS-negotiated key modes
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 7c3aaac..e153be7 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -429,6 +429,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 *
@@ -2721,10 +2723,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;
 }
@@ -2849,6 +2859,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.
@@ -2864,6 +2916,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;
 }

diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h
index 69ed85e..ec1e7ab 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.
  *
@@ -181,6 +188,8 @@ struct multi_context {
   /* mapping between inotify watch descriptors and multi_instances */
   struct hash *inotify_watchers;
 #endif
+
+  struct deferred_signal_schedule_entry deferred_shutdown_signal;
 };

 /*
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 901d710..1c522b8 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -469,6 +469,9 @@ static const char usage_message[] =
   "--stale-routes-check n [t] : Remove routes with a last activity timestamp\n"
   "                             older than n seconds. Run this check every t\n"
   "                             seconds (defaults to n).\n"
+  "--explicit-exit-notify [n] : In UDP server mode send [RESTART] command on 
exit/restart to connected\n"
+  "                             clients. n = 1 - reconnect to same server,\n"
+  "                             2 - advance to next server, default=1.\n"
 #if PORT_SHARE
   "--port-share host port [dir] : When run in TCP mode, proxy incoming HTTPS\n"
   "                  sessions to a web server at host:port.  dir specifies 
an\n"
@@ -2020,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 c642aa0..d1333d3 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -118,9 +118,7 @@ struct connection_entry
   int mssfix;            /* Upper bound on TCP MSS */
   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 */
-#endif
+  int explicit_exit_notification;  /* Explicitly tell peer when we are exiting 
via OCC_EXIT or [RESTART] message */

 # define CE_DISABLED (1<<0)
 # define CE_MAN_QUERY_PROXY (1<<1)
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index a4cb726..d4f3cb6 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -105,6 +105,7 @@ server_pushed_signal (struct context *c, const struct 
buffer *buffer, const bool
        m = BSTR (&buf);

       /* preserve cached passwords? */
+      /* advance to next server? */
       {
        bool purge = true;

@@ -115,6 +116,11 @@ server_pushed_signal (struct context *c, const struct 
buffer *buffer, const bool
              {
                if (m[i] == 'P')
                  purge = false;
+               else if (m[i] == 'N')
+                 {
+                   /* next server? */
+                   c->options.no_advance = false;
+                 }
              }
          }
        if (purge)
-- 
1.9.1


Reply via email to