Skip to contentSkip to conversation

Notify clients about server's exit/restart

PATCH Superseded Lev Stipakov 3 participants 7 messages Mar 2, 2015 – Nov 22, 2015

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 Notify clients about server's exit/restart

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(-)
src/openvpn/multi.c +62 -4
index 4412491..b0d0e08 100644
@@ -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;
 }
 
src/openvpn/multi.h +13 -1
index 32b89d2..d968626 100644
@@ -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.
src/openvpn/occ.c +8 -0
index ff48706..8302488 100644
@@ -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 */
 }
src/openvpn/occ.h +6 -0
index 5d88cc9..22ba4dc 100644
@@ -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.
src/openvpn/options.c +2 -5
index 4ea03d1..c00b5a9 100644
@@ -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)
src/openvpn/options.h +2 -1
index 7a8b21e..d232b83 100644
@@ -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)
Signature · 1 line
1.9.1
#1 lore raw

PATCH v3 1/1 Notify clients about server's exit/restart

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.

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

---
 doc/openvpn.8         | 15 ++++++++++--
 src/openvpn/multi.c   | 68 ++++++++++++++++++++++++++++++++++++++++++++++++---
 src/openvpn/multi.h   |  9 +++++++
 src/openvpn/options.c |  9 +++----
 src/openvpn/push.c    |  6 +++++
 5 files changed, 96 insertions(+), 11 deletions(-)
doc/openvpn.8 +13 -2
index e213f5a..0a00dec 100644
@@ -3829,8 +3829,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
src/openvpn/multi.c +65 -3

93 lines not on this page · raw message

src/openvpn/multi.h +9 -0
index 32b89d2..a5745d3 100644
@@ -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;
 };
 
 /*
src/openvpn/options.c +3 -6
index de4fa38..18acb3a 100644
@@ -493,8 +493,9 @@ static const char usage_message[] =
   "                  waiting for a response before trying the next server.\n"
 #endif
 #ifdef ENABLE_OCC
-  "--explicit-exit-notify [n] : On exit/restart, send exit signal to\n"
-  "                  server/remote. n = # of retries, default=1.\n"
+  "--explicit-exit-notify [n] : On exit/restart, send exit signal/restart command to\n"
+  "                  server/remote or client. In client mode: n = # of retries, default=1.\n"
+  "                  In server mode: 1 - reconnect to same server, 2 - advance to next server, default=1.\n"
 #endif
 #ifdef ENABLE_CRYPTO
   "\n"
@@ -2020,10 +2021,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)
src/openvpn/push.c +6 -0
index b9d0c4c..aed0a68 100644
@@ -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)
Signature · 1 line
1.9.1
#2 lore raw

Am 13.10.15 um 16:45 schrieb Lev Stipakov:

Quoted 8 lines

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.

Quoted 9 lines

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

This gets an ACK from me. As additional note, we should add a follow up that a server shutting down should not accept new clients. My client reconnects in the 2s shutdown interval.

Arne

#3 lore raw
In reply to Lev Stipakov

Hi,

Quoted 8 lines · Lev Stipakov

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.

Quoted 7 lines

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

Thanks for this. A few observations...

- it does not apply anymore, due to new code in multi.h (someone committed #ifdef ENABLE_ASYNC_PUSH in the meantime :-) ), so I'd appreciate a rebase

- I'm not totally happy about the #ifdef ENABLE_OCC and the fact that multi_push_restart_schedule_exit() is not #ifdef'ed as well (a smart compiler will optimize it away, as it is static and not called, but I'm not sure whether it is considered good style).

Since the new server side code does not actually *do* OCC any more we are just #ifdef'ing it to access options->ce.explicit_exit_notify because that one is only compiled in #ifdef ENABLE_OCC ... so we're coupling this new functionality to an #ifdef which is not really relevant.

No good suggestion yet, just expressing thoughts...

gert

Signature · 4 lines
USENET is *not* the non-clickable part of WWW!
                                                           //www.muc.de/~gert/
Gert Doering - Munich, Germany                             [email protected]...
fax: +49-89-35655025                        [email protected]...
#4 lore raw

Hi,

Quoted 8 lines

Since the new server side code does not actually *do* OCC any more we are just #ifdef'ing it to access options->ce.explicit_exit_notify because that one is only compiled in #ifdef ENABLE_OCC ... so we're coupling this new functionality to an #ifdef which is not really relevant.

No good suggestion yet, just expressing thoughts...

Since connection_entry->explicit_exit_notification is now used also outside of OCC context, I suggest to:

1) Remove #ifdef ENABLE_OCC around it's definition.

2) Fix comment there:

/* Explicitly tell peer when we are exiting via OCC_EXIT or control channel [RESTART] message */

3) Remove (unneeded anymore) #ifdef in multi_process_signal around accessing explicit_exit_notification.

What do you think?

-Lev

#5 lore raw

PATCH v4 1/1 Notify clients about server's exit/restart

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

---
 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(-)
doc/openvpn.8 +13 -2
index 2978b7f..dfb63fc 100644
@@ -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
src/openvpn/multi.c +63 -3

91 lines not on this page · raw message

src/openvpn/multi.h +9 -0
index 69ed85e..ec1e7ab 100644
@@ -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;
 };
 
 /*
src/openvpn/options.c +3 -4
index 901d710..1c522b8 100644
@@ -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)
src/openvpn/options.h +1 -3
index c642aa0..d1333d3 100644
@@ -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)
src/openvpn/push.c +6 -0
index a4cb726..d4f3cb6 100644
@@ -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)
Signature · 1 line
1.9.1
#6 lore raw

ACK, and thanks for your patience and persistance

Since v4 is functionally the same as v3 (just a few #ifdef ENABLE_OCC shuffled around, and different "adjacent" code due to rebasing), I've taken Arne's ACK as valid for v4, too - and add my own for the v3->v4 changes.

Your patch has been applied to the master branch.

Awaiting the client-side patch now :-) (and maybe something to make the server block incoming reconnects while in shut-down phase, see Arne's comment)

commit 8dd9ff8ca062de34bcd16e34847d106ee9b992ff (master)

Author: Lev Stipakov
Date:   Tue Nov 17 11:33:47 2015 +0200
     Notify clients about server's exit/restart

     Signed-off-by: Lev Stipakov <[email protected]...>
     Acked-by: Arne Schwabe <[email protected]...>
     Acked-by: Gert Doering <[email protected]...>
     Message-Id: <[email protected]...>
     URL: http://article.gmane.org/gmane.network.openvpn.devel/10515
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering