android: Fix kernel-net implementation on Android 4.3 and earlier
authorTobias Brunner <tobias@strongswan.org>
Mon, 22 Jun 2015 15:15:33 +0000 (17:15 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 28 Jul 2015 11:27:33 +0000 (13:27 +0200)
Before fwmarks were used protected sockets were bound to the outbound
interface via SO_BINDTODEVICE.  This does not always seem to work well
together with our connect()/getsockname() trick if the server is covered
by the traffic selectors.  Calling protect() again after disconnecting
the socket seems to help, but if there is no connectivity at all we still
get the virtual IP back (maybe protect() does not bind the socket to any
interface then).

src/frontends/android/jni/libandroidbridge/android_jni.h
src/frontends/android/jni/libandroidbridge/kernel/android_net.c

index 99c0bc2..b08670f 100644 (file)
@@ -54,6 +54,8 @@ typedef enum {
        ANDROID_ICE_CREAM_SANDWICH = 14,
        ANDROID_ICE_CREAM_SANDWICH_MR1 = 15,
        ANDROID_JELLY_BEAN = 16,
+       ANDROID_JELLY_BEAN_MR1 = 17,
+       ANDROID_JELLY_BEAN_MR2 = 18,
 } android_sdk_version_t;
 
 /**
index 9cab74e..73322ad 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "android_net.h"
 
+#include "../android_jni.h"
 #include "../charonservice.h"
 #include <hydra.h>
 #include <processing/jobs/callback_job.h>
@@ -114,6 +115,11 @@ METHOD(kernel_net_t, get_source_addr, host_t*,
                DBG1(DBG_KNL, "failed to disconnect socket: %s", strerror(errno));
                return NULL;
        }
+       if (android_sdk_version <= ANDROID_JELLY_BEAN_MR2)
+       {       /* this seems to help avoiding the VIP, unless there is no connectivity
+                * at all */
+               charonservice->bypass_socket(charonservice, -1, 0);
+       }
        if (connect(this->socket_v4, dest->get_sockaddr(dest), addrlen) < 0)
        {
                /* don't report an error if we are not connected (ENETUNREACH) */
@@ -131,6 +137,29 @@ METHOD(kernel_net_t, get_source_addr, host_t*,
        return host_create_from_sockaddr((sockaddr_t*)&addr);
 }
 
+METHOD(kernel_net_t, get_source_addr_old, host_t*,
+       private_android_net_t *this, host_t *dest, host_t *src)
+{
+       host_t *host;
+
+       /* on older Android versions we might get the virtual IP back because
+        * the protect() implementation there and connect() don't properly work
+        * together, on newer releases (using fwmarks) that's not a problem */
+       host = get_source_addr(this, dest, src);
+       if (host)
+       {
+               this->mutex->lock(this->mutex);
+               if (this->vips->find_first(this->vips, (void*)host->ip_equals,
+                                                                  NULL, host) == SUCCESS)
+               {
+                       host->destroy(host);
+                       host = NULL;
+               }
+               this->mutex->unlock(this->mutex);
+       }
+       return host;
+}
+
 METHOD(kernel_net_t, get_nexthop, host_t*,
        private_android_net_t *this, host_t *dest, int prefix, host_t *src)
 {
@@ -233,6 +262,11 @@ kernel_net_t *kernel_android_net_create()
        );
        timerclear(&this->next_roam);
 
+       if (android_sdk_version <= ANDROID_JELLY_BEAN_MR2)
+       {
+               this->public.get_source_addr = _get_source_addr_old;
+       }
+
        this->socket_v4 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if (this->socket_v4 < 0)
        {