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


Reply via email to