]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Workaround broken Android 4.4 VpnService API for persist-tun mode
authorArne Schwabe <arne@rfc2549.org>
Fri, 21 Mar 2014 13:18:36 +0000 (14:18 +0100)
committerGert Doering <gert@greenie.muc.de>
Sat, 22 Mar 2014 18:16:41 +0000 (19:16 +0100)
In Android 4.4 it is not possible to open a new tun device and then close
the old tun device without breaking the whole VPNService stack until the
device is rebooted.

Add new management method to ask the UI what method should be taken to
ensure the optimal solution for the situation.  Then do open-before-close
or close-before-open inside open_tun() as requested.

Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <1395407925-25518-4-git-send-email-arne@rfc2549.org>
URL: http://article.gmane.org/gmane.network.openvpn.devel/8373

Signed-off-by: Gert Doering <gert@greenie.muc.de>
src/openvpn/init.c
src/openvpn/manage.c
src/openvpn/manage.h
src/openvpn/tun.c

index c50b5c2609fd6de47ae8df4153c123f0d3737e91..21e305274816d85f669e67617da294f401d31fbc 100644 (file)
@@ -1426,10 +1426,10 @@ do_open_tun (struct context *c)
 
 #ifdef TARGET_ANDROID
       /* If we emulate persist-tun on android we still have to open a new tun and
-         then close the old */
+       * then close the old */
       int oldtunfd=-1;
       if (c->c1.tuntap)
-          oldtunfd = c->c1.tuntap->fd;
+       oldtunfd = c->c1.tuntap->fd;
 #endif
 
       /* initialize (but do not open) tun/tap object */
@@ -1463,14 +1463,14 @@ do_open_tun (struct context *c)
         do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list,
                   c->c1.tuntap, c->plugins, c->c2.es);
       }
-
+#ifdef TARGET_ANDROID
+       /* Store the old fd inside the fd so open_tun can use it */
+       c->c1.tuntap->fd = oldtunfd;
+#endif
       /* open the tun device */
       open_tun (c->options.dev, c->options.dev_type, c->options.dev_node,
                c->c1.tuntap);
-#ifdef TARGET_ANDROID
-      if (oldtunfd>=0)
-        close(oldtunfd);
-#endif
+
       /* set the hardware address */
       if (c->options.lladdr)
          set_lladdr(c->c1.tuntap->actual_name, c->options.lladdr, c->c2.es);
@@ -3767,7 +3767,10 @@ close_context (struct context *c, int sig, unsigned int flags)
     {
       if ((flags & CC_USR1_TO_HUP)
          || (c->sig->source == SIG_SOURCE_HARD && (flags & CC_HARD_USR1_TO_HUP)))
-       c->sig->signal_received = SIGHUP;
+        {
+          c->sig->signal_received = SIGHUP;
+          c->sig->signal_text = "close_context usr1 to hup";
+        }
     }
 
   if (!(flags & CC_NO_CLOSE))
index 22dbe1305ec69b8d28a85fd23fb67a22585383c5..8097905ce345c17a65424bc720d9718b4e4c9f7a 100644 (file)
@@ -1864,6 +1864,34 @@ bool management_android_control (struct management *man, const char *command, co
   management_query_user_pass(management, &up , command, GET_USER_PASS_NEED_OK,(void*) 0);
   return strcmp ("ok", up.password)==0;
 }
+
+/*
+ * In Android 4.4 it is not possible to open a new tun device and then close the
+ * old tun device without breaking the whole VPNService stack until the device
+ * is rebooted. This management method ask the UI what method should be taken to
+ * ensure the optimal solution for the situation
+ */
+int managment_android_persisttun_action (struct management *man)
+{
+  struct user_pass up;
+  CLEAR(up);
+  strcpy(up.username,"tunmethod");
+  management_query_user_pass(management, &up , "PERSIST_TUN_ACTION",
+                            GET_USER_PASS_NEED_OK,(void*) 0);
+  if (!strcmp("NOACTION", up.password))
+    return ANDROID_KEEP_OLD_TUN;
+  else if (!strcmp ("OPEN_AFTER_CLOSE", up.password))
+    return ANDROID_OPEN_AFTER_CLOSE;
+  else if (!strcmp ("OPEN_BEFORE_CLOSE", up.password))
+    return ANDROID_OPEN_BEFORE_CLOSE;
+  else
+    msg (M_ERR, "Got unrecognised '%s' from management for PERSIST_TUN_ACTION query", up.password);
+
+  ASSERT(0);
+  return ANDROID_OPEN_AFTER_CLOSE;
+}
+
+
 #endif
 
 static int
index 962b5bc41930e98be4bddd55d3e65e0ee7289bec..1c8dda6911c045c55aea9dcc9c737cb4e401625e 100644 (file)
@@ -378,6 +378,11 @@ bool management_query_user_pass (struct management *man,
 
 #ifdef TARGET_ANDROID
 bool management_android_control (struct management *man, const char *command, const char *msg);
+
+#define ANDROID_KEEP_OLD_TUN 1
+#define ANDROID_OPEN_AFTER_CLOSE 2
+#define ANDROID_OPEN_BEFORE_CLOSE 3
+int managment_android_persisttun_action (struct management *man);
 #endif
 
 bool management_should_daemonize (struct management *man);
index 672486e454ccf916a954862ad4fb8b73349cc838..4df271d5ba151d25e8ef30d01acaa1fc2972a305 100644 (file)
@@ -1501,19 +1501,38 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu
   struct gc_arena gc = gc_new ();
   bool opentun;
 
+  int oldtunfd = tt->fd;
+
   for (i = 0; i < tt->options.dns_len; ++i) {
     management_android_control (management, "DNSSERVER",
-                                print_in_addr_t(tt->options.dns[i], 0, &gc));
+                               print_in_addr_t(tt->options.dns[i], 0, &gc));
   }
 
   if(tt->options.domain)
     management_android_control (management, "DNSDOMAIN", tt->options.domain);
 
-  opentun = management_android_control (management, "OPENTUN", dev);
+  int android_method = managment_android_persisttun_action (management);
+
+  /* Android 4.4 workaround */
+  if (oldtunfd >=0 && android_method == ANDROID_OPEN_AFTER_CLOSE)
+    {
+      close(oldtunfd);
+      openvpn_sleep(2);
+    }
+
+  if (oldtunfd >=0  && android_method == ANDROID_KEEP_OLD_TUN) {
+    /* keep the old fd */
+    opentun = true;
+  } else {
+    opentun = management_android_control (management, "OPENTUN", dev);
+    /* Pick up the fd from management interface after calling the
+     * OPENTUN command */
+    tt->fd = management->connection.lastfdreceived;
+    management->connection.lastfdreceived=-1;
+  }
 
-  /* Pick up the fd from management interface after calling the OPENTUN command */
-  tt->fd = management->connection.lastfdreceived;
-  management->connection.lastfdreceived=-1;
+  if (oldtunfd>=0 && android_method == ANDROID_OPEN_BEFORE_CLOSE)
+    close(oldtunfd);
 
   /* Set the actual name to a dummy name */
   tt->actual_name = string_alloc (ANDROID_TUNNAME, NULL);