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).
This commit is contained in:
Tobias Brunner 2015-07-21 11:14:53 +02:00
parent 41b59a3443
commit 030bcee1b0
1 changed files with 35 additions and 2 deletions

View File

@ -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;
}