From 21db9f0c0dc80c9e71b80655a703123f12b9b46c Mon Sep 17 00:00:00 2001
From: roychang <roychang@synology.com>
Date: Tue, 2 Sep 2014 19:03:01 +0800
Subject: <VPN Server> #227 - Support kill command for IPv6 client

	- Add port info of IPv6 client in status command

Reviewer: benchang
---
 src/openvpn/manage.c |   43 +++++++++++++++++++++++++++++++++++++++++--
 src/openvpn/manage.h |    1 +
 src/openvpn/mroute.c |   10 ++++++++++
 src/openvpn/multi.c  |   32 ++++++++++++++++++++++++++++++++
 4 files changed, 84 insertions(+), 2 deletions(-)

diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c
index 0a4542a..9416a8e 100644
--- a/src/openvpn/manage.c
+++ b/src/openvpn/manage.c
@@ -454,18 +454,57 @@ man_kill (struct management *man, const char *victim)
 {
   struct gc_arena gc = gc_new ();
 
-  if (man->persist.callback.kill_by_cn && man->persist.callback.kill_by_addr)
+  if (man->persist.callback.kill_by_cn && man->persist.callback.kill_by_addr && man->persist.callback.kill_by_addr6)
     {
       struct buffer buf;
       char p1[128];
       char p2[128];
+      char p3[128];
+      char p4[128];
       int n_killed;
 
       buf_set_read (&buf, (uint8_t*) victim, strlen (victim) + 1);
       buf_parse (&buf, ':', p1, sizeof (p1));
       buf_parse (&buf, ':', p2, sizeof (p2));
 
-      if (strlen (p1) && strlen (p2))
+      buf_set_read (&buf, (uint8_t*) victim, strlen (victim) + 1);
+      buf_parse (&buf, '(', p3, sizeof (p3));
+      buf_parse (&buf, ')', p4, sizeof (p4));
+
+      if (strlen (p3) && strlen (p4))
+	{
+	  /* IPv6(port) specified */
+	  int s;
+	  struct in6_addr addr;
+
+	  s = inet_pton(AF_INET6, p3, &addr);
+	  if (s > 0)
+	    {
+	      const int port = atoi (p4);
+	      if (port > 0 && port < 65536)
+		{
+		  n_killed = (*man->persist.callback.kill_by_addr6) (man->persist.callback.arg, addr, port);
+		  if (n_killed > 0)
+		    {
+		      msg (M_CLIENT, "SUCCESS: %d client(s) at address %s:%d killed",
+			   n_killed,
+			   print_in6_addr (addr, 0, &gc),
+			   port);
+		    }
+		  else
+		    {
+		      msg (M_CLIENT, "ERROR: client at address %s:%d not found",
+			   print_in6_addr (addr, 0, &gc),
+			   port);
+		    }
+		}
+	    }
+	  else
+	    {
+	      msg (M_CLIENT, "ERROR: error parsing IPv6 address: %s", p3);
+	    }
+	}
+      else if (strlen (p1) && strlen (p2))
 	{
 	  /* IP:port specified */
 	  bool status;
diff --git a/src/openvpn/manage.h b/src/openvpn/manage.h
index 28da69f..fcae53b 100644
--- a/src/openvpn/manage.h
+++ b/src/openvpn/manage.h
@@ -153,6 +153,7 @@ struct management_callback
   void (*show_net) (void *arg, const int msglevel);
   int (*kill_by_cn) (void *arg, const char *common_name);
   int (*kill_by_addr) (void *arg, const in_addr_t addr, const int port);
+  int (*kill_by_addr6) (void *arg, const struct in6_addr addr, const int port);
   void (*delete_event) (void *arg, event_t event);
   int (*n_clients) (void *arg);
 #ifdef MANAGEMENT_DEF_AUTH
diff --git a/src/openvpn/mroute.c b/src/openvpn/mroute.c
index 850e336..8ee503b 100644
--- a/src/openvpn/mroute.c
+++ b/src/openvpn/mroute.c
@@ -426,12 +426,22 @@ mroute_addr_print_ex (const struct mroute_addr *ma,
 	  break;
 	case MR_ADDR_IPV6:
 	  {
+	    struct buffer buf;
+	    int port;
+	    buf_set_read (&buf, maddr.addr, maddr.len);
+	    buf_read_alloc (&buf, 16);
 	    buf_printf (&out, "%s",
 		  print_in6_addr( *(struct in6_addr*)&maddr.addr, 0, gc)); 
 	    if (maddr.type & MR_WITH_NETBITS)
 	      {
 		buf_printf (&out, "/%d", maddr.netbits);
 	      }
+	    if (maddr.type & MR_WITH_PORT)
+	      {
+		port = buf_read_u16 (&buf);
+		if (port >= 0)
+		  buf_printf (&out, "(%d)", port);
+	      }
 	    }
 	    break;
 	  default:
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 1db7bea..2efd040 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -2699,6 +2699,37 @@ management_callback_kill_by_addr (void *arg, const in_addr_t addr, const int por
   return count;
 }
 
+static int
+management_callback_kill_by_addr6 (void *arg, const struct in6_addr addr, const int port)
+{
+  struct multi_context *m = (struct multi_context *) arg;
+  struct hash_iterator hi;
+  struct hash_element *he;
+  struct openvpn_sockaddr saddr;
+  struct mroute_addr maddr;
+  int count = 0;
+
+  CLEAR (saddr);
+  saddr.addr.in6.sin6_family = AF_INET6;
+  saddr.addr.in6.sin6_addr = addr;
+  saddr.addr.in6.sin6_port = htons (port);
+  if (mroute_extract_openvpn_sockaddr (&maddr, &saddr, true))
+    {
+      hash_iterator_init (m->iter, &hi);
+      while ((he = hash_iterator_next (&hi)))
+	{
+	  struct multi_instance *mi = (struct multi_instance *) he->value;
+	  if (!mi->halt && mroute_addr_equal (&maddr, &mi->real))
+	    {
+	      multi_signal_instance (m, mi, SIGTERM);
+	      ++count;
+	    }
+	}
+      hash_iterator_free (&hi);
+    }
+  return count;
+}
+
 static void
 management_delete_event (void *arg, event_t event)
 {
@@ -2830,6 +2861,7 @@ init_management_callback_multi (struct multi_context *m)
       cb.show_net = management_show_net_callback;
       cb.kill_by_cn = management_callback_kill_by_cn;
       cb.kill_by_addr = management_callback_kill_by_addr;
+      cb.kill_by_addr6 = management_callback_kill_by_addr6;
       cb.delete_event = management_delete_event;
       cb.n_clients = management_callback_n_clients;
 #ifdef MANAGEMENT_DEF_AUTH
-- 
1.7.9.5

