From 030bcee1b0f8cebce7cce1a87e33b98f3e1ed784 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 21 Jul 2015 11:14:53 +0200 Subject: [PATCH] android: Retrigger a roam event if reportedly connected but no source address found In dual-stack environments the IPv6 connectivity (via autoconfiguration) might be established before the IPv4 connectivity (via DHCP). It seems Android triggers the CONNECTIVITY_ACTION broadcast already when the first family is fully configured. At that time we might not be able to find an IPv4 source address. And since Android does not trigger the broadcast again if IPv4 connectivity is established, the connection is broken afterwards. So we store the connectivity state and if we are reportedly connected but still find no source address we trigger a roam event to recheck for an IPv4 address. This will cause regular rechecks if a device enters an IPv6-only network, but I guess that's rare (otherwise we could limit the number of rechecks done between connectivity changes). --- .../jni/libandroidbridge/kernel/android_net.c | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c index 73322ad76..2ce1bdfac 100644 --- a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c +++ b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c @@ -26,6 +26,7 @@ /** delay before firing roam events (ms) */ #define ROAM_DELAY 100 +#define ROAM_DELAY_RECHECK 1000 typedef struct private_android_net_t private_android_net_t; @@ -60,6 +61,11 @@ struct private_android_net_t { * Socket used to determine source address */ int socket_v4; + + /** + * Whether the device is currently connected + */ + bool connected; }; /** @@ -84,6 +90,7 @@ static void connectivity_cb(private_android_net_t *this, time_monotonic(&now); this->mutex->lock(this->mutex); + this->connected = !disconnected; if (!timercmp(&now, &this->next_roam, >)) { this->mutex->unlock(this->mutex); @@ -107,6 +114,8 @@ METHOD(kernel_net_t, get_source_addr, host_t*, struct sockaddr_in6 sin6; } addr; socklen_t addrlen; + timeval_t now; + job_t *job; addrlen = *dest->get_sockaddr_len(dest); addr.sockaddr.sa_family = AF_UNSPEC; @@ -127,6 +136,27 @@ METHOD(kernel_net_t, get_source_addr, host_t*, { DBG1(DBG_KNL, "failed to connect socket: %s", strerror(errno)); } + else + { + time_monotonic(&now); + this->mutex->lock(this->mutex); + if (this->connected && timercmp(&now, &this->next_roam, >)) + { /* we were not able to find a source address but reportedly are + * connected, trigger a recheck in case an IP address appears + * delayed but the callback is not triggered again */ + timeval_add_ms(&now, ROAM_DELAY_RECHECK); + this->next_roam = now; + this->mutex->unlock(this->mutex); + job = (job_t*)callback_job_create((callback_job_cb_t)roam_event, + NULL, NULL, NULL); + lib->scheduler->schedule_job_ms(lib->scheduler, job, + ROAM_DELAY_RECHECK); + } + else + { + this->mutex->unlock(this->mutex); + } + } return NULL; } if (getsockname(this->socket_v4, &addr.sockaddr, &addrlen) < 0) @@ -275,7 +305,10 @@ kernel_net_t *kernel_android_net_create() } charonservice->bypass_socket(charonservice, this->socket_v4, AF_INET); - this->network_manager->add_connectivity_cb(this->network_manager, - (void*)connectivity_cb, this); + this->mutex->lock(this->mutex); + this->network_manager->add_connectivity_cb( + this->network_manager, (void*)connectivity_cb, this); + this->connected = this->network_manager->is_connected(this->network_manager); + this->mutex->unlock(this->mutex); return &this->public; }