diff options
Diffstat (limited to 'src/frontends/android')
13 files changed, 613 insertions, 102 deletions
diff --git a/src/frontends/android/AndroidManifest.xml b/src/frontends/android/AndroidManifest.xml index f0ed02ff3..4655cd7fc 100644 --- a/src/frontends/android/AndroidManifest.xml +++ b/src/frontends/android/AndroidManifest.xml @@ -23,6 +23,7 @@ <uses-sdk android:minSdkVersion="14" /> <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <application android:icon="@drawable/ic_launcher" diff --git a/src/frontends/android/jni/Android.mk b/src/frontends/android/jni/Android.mk index 326565f2b..40a7a06a3 100644 --- a/src/frontends/android/jni/Android.mk +++ b/src/frontends/android/jni/Android.mk @@ -16,11 +16,14 @@ openssl_PATH := $(LOCAL_PATH)/openssl/include # CFLAGS (partially from a configure run using droid-gcc) strongswan_CFLAGS := \ + -Wall \ + -Wextra \ -Wno-format \ -Wno-pointer-sign \ -Wno-pointer-arith \ -Wno-sign-compare \ -Wno-strict-aliasing \ + -Wno-unused-parameter \ -DHAVE___BOOL \ -DHAVE_STDBOOL_H \ -DHAVE_ALLOCA_H \ diff --git a/src/frontends/android/jni/libandroidbridge/Android.mk b/src/frontends/android/jni/libandroidbridge/Android.mk index f47e39656..b1fa4d1cb 100644 --- a/src/frontends/android/jni/libandroidbridge/Android.mk +++ b/src/frontends/android/jni/libandroidbridge/Android.mk @@ -11,6 +11,7 @@ backend/android_service.c backend/android_service.h \ charonservice.c charonservice.h \ kernel/android_ipsec.c kernel/android_ipsec.h \ kernel/android_net.c kernel/android_net.h \ +kernel/network_manager.c kernel/network_manager.h \ vpnservice_builder.c vpnservice_builder.h # build libandroidbridge ------------------------------------------------------- diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index 2a115d2f9..b00567f60 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -59,11 +59,6 @@ struct private_android_service_t { char *type; /** - * local ipv4 address - */ - char *local_address; - - /** * gateway */ char *gateway; @@ -362,7 +357,6 @@ METHOD(listener_t, child_updown, bool, { /* disable the hooks registered to catch initiation failures */ this->public.listener.ike_updown = NULL; - this->public.listener.ike_state_change = NULL; if (!setup_tun_device(this, ike_sa, child_sa)) { DBG1(DBG_DMN, "failed to setup TUN device"); @@ -403,19 +397,6 @@ METHOD(listener_t, ike_updown, bool, return TRUE; } -METHOD(listener_t, ike_state_change, bool, - private_android_service_t *this, ike_sa_t *ike_sa, ike_sa_state_t state) -{ - /* this call back is only registered during initiation */ - if (this->ike_sa == ike_sa && state == IKE_DESTROYING) - { - charonservice->update_status(charonservice, - CHARONSERVICE_UNREACHABLE_ERROR); - return FALSE; - } - return TRUE; -} - METHOD(listener_t, alert, bool, private_android_service_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args) @@ -432,6 +413,15 @@ METHOD(listener_t, alert, bool, charonservice->update_status(charonservice, CHARONSERVICE_PEER_AUTH_ERROR); break; + case ALERT_PEER_INIT_UNREACHABLE: + this->lock->read_lock(this->lock); + if (this->tunfd < 0) + { /* only handle this if we are not reestablishing the SA */ + charonservice->update_status(charonservice, + CHARONSERVICE_UNREACHABLE_ERROR); + } + this->lock->unlock(this->lock); + break; default: break; } @@ -455,9 +445,8 @@ METHOD(listener_t, ike_reestablish, bool, if (this->ike_sa == old) { this->ike_sa = new; - /* re-register hooks to detect initiation failures */ + /* re-register hook to detect initiation failures */ this->public.listener.ike_updown = _ike_updown; - this->public.listener.ike_state_change = _ike_state_change; /* the TUN device will be closed when the new CHILD_SA is established */ } return TRUE; @@ -480,13 +469,13 @@ static job_requeue_t initiate(private_android_service_t *this) } }; - ike_cfg = ike_cfg_create(TRUE, TRUE, this->local_address, FALSE, + ike_cfg = ike_cfg_create(TRUE, TRUE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), this->gateway, FALSE, IKEV2_UDP_PORT); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("android", IKEV2, ike_cfg, CERT_SEND_IF_ASKED, - UNIQUE_REPLACE, 1, /* keyingtries */ + UNIQUE_REPLACE, 0, /* keyingtries */ 36000, 0, /* rekey 10h, reauth none */ 600, 600, /* jitter, over 10min */ TRUE, FALSE, /* mobike, aggressive */ @@ -538,10 +527,14 @@ static job_requeue_t initiate(private_android_service_t *this) peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE); child_cfg = child_cfg_create("android", &lifetime, NULL, TRUE, MODE_TUNNEL, - ACTION_NONE, ACTION_NONE, ACTION_NONE, FALSE, - 0, 0, NULL, NULL, 0); - child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP)); - ts = traffic_selector_create_dynamic(0, 0, 65535); + ACTION_NONE, ACTION_RESTART, ACTION_RESTART, + FALSE, 0, 0, NULL, NULL, 0); + /* create an ESP proposal with the algorithms currently supported by + * libipsec, no PFS for now */ + child_cfg->add_proposal(child_cfg, proposal_create_from_string(PROTO_ESP, + "aes128-aes192-aes256-sha1-sha256-sha384-sha512")); + ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE, "0.0.0.0", + 0, "255.255.255.255", 65535); child_cfg->add_traffic_selector(child_cfg, TRUE, ts); ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE, "0.0.0.0", 0, "255.255.255.255", 65535); @@ -588,7 +581,6 @@ METHOD(android_service_t, destroy, void, close_tun_device(this); this->lock->destroy(this->lock); free(this->type); - free(this->local_address); free(this->gateway); free(this->username); if (this->password) @@ -603,8 +595,8 @@ METHOD(android_service_t, destroy, void, * See header */ android_service_t *android_service_create(android_creds_t *creds, char *type, - char *local_address, char *gateway, - char *username, char *password) + char *gateway, char *username, + char *password) { private_android_service_t *this; @@ -614,14 +606,12 @@ android_service_t *android_service_create(android_creds_t *creds, char *type, .ike_rekey = _ike_rekey, .ike_reestablish = _ike_reestablish, .ike_updown = _ike_updown, - .ike_state_change = _ike_state_change, .child_updown = _child_updown, .alert = _alert, }, .destroy = _destroy, }, .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), - .local_address = local_address, .username = username, .password = password, .gateway = gateway, diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.h b/src/frontends/android/jni/libandroidbridge/backend/android_service.h index 52c3dc5c8..1bfdcf994 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.h +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.h @@ -53,13 +53,12 @@ struct android_service_t { * * @param creds Android specific credential set * @param type VPN type (see VpnType.java) - * @param local_address local ip address * @param gateway gateway address * @param username user name (local identity) * @param password password (if any) */ android_service_t *android_service_create(android_creds_t *creds, char *type, - char *local_address, char *gateway, - char *username, char *password); + char *gateway, char *username, + char *password); #endif /** ANDROID_SERVICE_H_ @}*/ diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.c b/src/frontends/android/jni/libandroidbridge/charonservice.c index ef4d42edf..1fde9887b 100644 --- a/src/frontends/android/jni/libandroidbridge/charonservice.c +++ b/src/frontends/android/jni/libandroidbridge/charonservice.c @@ -38,7 +38,7 @@ #define ANDROID_DEBUG_LEVEL 1 #define ANDROID_RETRASNMIT_TRIES 3 -#define ANDROID_RETRANSMIT_TIMEOUT 3.0 +#define ANDROID_RETRANSMIT_TIMEOUT 2.0 #define ANDROID_RETRANSMIT_BASE 1.4 typedef struct private_charonservice_t private_charonservice_t; @@ -74,9 +74,19 @@ struct private_charonservice_t { vpnservice_builder_t *builder; /** + * NetworkManager instance (accessed via JNI) + */ + network_manager_t *network_manager; + + /** * CharonVpnService reference */ jobject vpn_service; + + /** + * Sockets that were bypassed and we keep track for + */ + linked_list_t *sockets; }; /** @@ -172,8 +182,10 @@ failed: return success; } -METHOD(charonservice_t, bypass_socket, bool, - private_charonservice_t *this, int fd, int family) +/** + * Bypass a single socket + */ +static bool bypass_single_socket(intptr_t fd, private_charonservice_t *this) { JNIEnv *env; jmethodID method_id; @@ -188,7 +200,7 @@ METHOD(charonservice_t, bypass_socket, bool, } if (!(*env)->CallBooleanMethod(env, this->vpn_service, method_id, fd)) { - DBG1(DBG_CFG, "VpnService.protect() failed"); + DBG2(DBG_KNL, "VpnService.protect() failed"); goto failed; } androidjni_detach_thread(); @@ -200,6 +212,19 @@ failed: return FALSE; } +METHOD(charonservice_t, bypass_socket, bool, + private_charonservice_t *this, int fd, int family) +{ + if (fd >= 0) + { + this->sockets->insert_last(this->sockets, (void*)(intptr_t)fd); + return bypass_single_socket((intptr_t)fd, this); + } + this->sockets->invoke_function(this->sockets, (void*)bypass_single_socket, + this); + return TRUE; +} + /** * Converts the given Java array of byte arrays (byte[][]) to a linked list * of chunk_t objects. @@ -330,22 +355,26 @@ METHOD(charonservice_t, get_vpnservice_builder, vpnservice_builder_t*, return this->builder; } +METHOD(charonservice_t, get_network_manager, network_manager_t*, + private_charonservice_t *this) +{ + return this->network_manager; +} + /** * Initiate a new connection * - * @param local local ip address (gets owned) * @param gateway gateway address (gets owned) * @param username username (gets owned) * @param password password (gets owned) */ -static void initiate(char *type, char *local, char *gateway, - char *username, char *password) +static void initiate(char *type, char *gateway, char *username, char *password) { private_charonservice_t *this = (private_charonservice_t*)charonservice; this->creds->clear(this->creds); DESTROY_IF(this->service); - this->service = android_service_create(this->creds, type, local, gateway, + this->service = android_service_create(this->creds, type, gateway, username, password); } @@ -400,10 +429,13 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder) .get_user_certificate = _get_user_certificate, .get_user_key = _get_user_key, .get_vpnservice_builder = _get_vpnservice_builder, + .get_network_manager = _get_network_manager, }, .attr = android_attr_create(), .creds = android_creds_create(), .builder = vpnservice_builder_create(builder), + .network_manager = network_manager_create(service), + .sockets = linked_list_create(), .vpn_service = (*env)->NewGlobalRef(env, service), ); charonservice = &this->public; @@ -439,6 +471,8 @@ static void charonservice_deinit(JNIEnv *env) { private_charonservice_t *this = (private_charonservice_t*)charonservice; + this->network_manager->destroy(this->network_manager); + this->sockets->destroy(this->sockets); this->builder->destroy(this->builder); this->creds->destroy(this->creds); this->attr->destroy(this->attr); @@ -555,16 +589,14 @@ JNI_METHOD(CharonVpnService, deinitializeCharon, void) * Initiate SA */ JNI_METHOD(CharonVpnService, initiate, void, - jstring jtype, jstring jlocal_address, jstring jgateway, jstring jusername, - jstring jpassword) + jstring jtype, jstring jgateway, jstring jusername, jstring jpassword) { - char *type, *local_address, *gateway, *username, *password; + char *type, *gateway, *username, *password; type = androidjni_convert_jstring(env, jtype); - local_address = androidjni_convert_jstring(env, jlocal_address); gateway = androidjni_convert_jstring(env, jgateway); username = androidjni_convert_jstring(env, jusername); password = androidjni_convert_jstring(env, jpassword); - initiate(type, local_address, gateway, username, password); + initiate(type, gateway, username, password); } diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.h b/src/frontends/android/jni/libandroidbridge/charonservice.h index 376f55014..4b5839f5e 100644 --- a/src/frontends/android/jni/libandroidbridge/charonservice.h +++ b/src/frontends/android/jni/libandroidbridge/charonservice.h @@ -32,6 +32,7 @@ #define CHARONSERVICE_H_ #include "vpnservice_builder.h" +#include "kernel/network_manager.h" #include <library.h> #include <utils/linked_list.h> @@ -69,7 +70,9 @@ struct charonservice_t { /** * Install a bypass policy for the given socket using the protect() Method - * of the Android VpnService interface + * of the Android VpnService interface. + * + * Use -1 as fd to re-bypass previously bypassed sockets. * * @param fd socket file descriptor * @param family socket protocol family @@ -111,6 +114,12 @@ struct charonservice_t { */ vpnservice_builder_t *(*get_vpnservice_builder)(charonservice_t *this); + /** + * Get the current network_manager_t object + * + * @return NetworkManager instance + */ + network_manager_t *(*get_network_manager)(charonservice_t *this); }; /** diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c b/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c index 08cc61610..85fe5d4c1 100644 --- a/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c +++ b/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c @@ -79,7 +79,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t, u_int16_t cpi, host_t *src, host_t *dst, host_t *new_src, host_t *new_dst, bool encap, bool new_encap, mark_t mark) { - return NOT_SUPPORTED; + return ipsec->sas->update_sa(ipsec->sas, spi, protocol, cpi, src, dst, + new_src, new_dst, encap, new_encap, mark); } METHOD(kernel_ipsec_t, query_sa, status_t, diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c index e29f95510..430c95bc8 100644 --- a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c +++ b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c @@ -14,6 +14,15 @@ #include "android_net.h" +#include "../charonservice.h" +#include <hydra.h> +#include <debug.h> +#include <processing/jobs/callback_job.h> +#include <threading/mutex.h> + +/** delay before firing roam events (ms) */ +#define ROAM_DELAY 100 + typedef struct private_kernel_android_net_t private_kernel_android_net_t; struct private_kernel_android_net_t { @@ -22,8 +31,66 @@ struct private_kernel_android_net_t { * Public kernel interface */ kernel_android_net_t public; + + /** + * Reference to NetworkManager object + */ + network_manager_t *network_manager; + + /** + * earliest time of the next roam event + */ + timeval_t next_roam; + + /** + * mutex to check and update roam event time + */ + mutex_t *mutex; }; +/** + * callback function that raises the delayed roam event + */ +static job_requeue_t roam_event() +{ + /* this will fail if no connection is up */ + charonservice->bypass_socket(charonservice, -1, 0); + hydra->kernel_interface->roam(hydra->kernel_interface, TRUE); + return JOB_REQUEUE_NONE; +} + +/** + * Listen for connectivity change events and queue a roam event + */ +static void connectivity_cb(private_kernel_android_net_t *this, + bool disconnected) +{ + timeval_t now; + job_t *job; + + time_monotonic(&now); + this->mutex->lock(this->mutex); + if (!timercmp(&now, &this->next_roam, >)) + { + this->mutex->unlock(this->mutex); + return; + } + timeval_add_ms(&now, ROAM_DELAY); + 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); +} + +METHOD(kernel_net_t, get_source_addr, host_t*, + private_kernel_android_net_t *this, host_t *dest, host_t *src) +{ + return this->network_manager->get_local_address(this->network_manager, + dest->get_family(dest) == AF_INET); +} + METHOD(kernel_net_t, add_ip, status_t, private_kernel_android_net_t *this, host_t *virtual_ip, host_t *iface_ip) { @@ -34,6 +101,9 @@ METHOD(kernel_net_t, add_ip, status_t, METHOD(kernel_net_t, destroy, void, private_kernel_android_net_t *this) { + this->network_manager->remove_connectivity_cb(this->network_manager, + (void*)connectivity_cb); + this->mutex->destroy(this->mutex); free(this); } @@ -47,7 +117,7 @@ kernel_android_net_t *kernel_android_net_create() INIT(this, .public = { .interface = { - .get_source_addr = (void*)return_null, + .get_source_addr = _get_source_addr, .get_nexthop = (void*)return_null, .get_interface = (void*)return_null, .create_address_enumerator = (void*)enumerator_create_empty, @@ -58,7 +128,12 @@ kernel_android_net_t *kernel_android_net_create() .destroy = _destroy, }, }, + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .network_manager = charonservice->get_network_manager(charonservice), ); + timerclear(&this->next_roam); + this->network_manager->add_connectivity_cb(this->network_manager, + (void*)connectivity_cb, this); return &this->public; }; diff --git a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c new file mode 100644 index 000000000..9c97fbb14 --- /dev/null +++ b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "network_manager.h" + +#include "../android_jni.h" +#include "../charonservice.h" +#include <debug.h> +#include <threading/mutex.h> + +typedef struct private_network_manager_t private_network_manager_t; + +struct private_network_manager_t { + + /** + * Public interface + */ + network_manager_t public; + + /** + * Reference to NetworkManager object + */ + jobject obj; + + /** + * Java class for NetworkManager + */ + jclass cls; + + /** + * Registered callback + */ + struct { + connectivity_cb_t cb; + void *data; + } connectivity_cb; + + /** + * Mutex to access callback + */ + mutex_t *mutex; +}; + +METHOD(network_manager_t, get_local_address, host_t*, + private_network_manager_t *this, bool ipv4) +{ + JNIEnv *env; + jmethodID method_id; + jstring jaddr; + char *addr; + host_t *host; + + androidjni_attach_thread(&env); + method_id = (*env)->GetMethodID(env, this->cls, "getLocalAddress", + "(Z)Ljava/lang/String;"); + if (!method_id) + { + goto failed; + } + jaddr = (*env)->CallObjectMethod(env, this->obj, method_id, ipv4); + if (!jaddr) + { + goto failed; + } + addr = androidjni_convert_jstring(env, jaddr); + androidjni_detach_thread(); + host = host_create_from_string(addr, 0); + free(addr); + return host; + +failed: + androidjni_exception_occurred(env); + androidjni_detach_thread(); + return NULL; +} + +JNI_METHOD(NetworkManager, networkChanged, void, + bool disconnected) +{ + private_network_manager_t *nm; + + nm = (private_network_manager_t*)charonservice->get_network_manager( + charonservice); + nm->mutex->lock(nm->mutex); + if (nm->connectivity_cb.cb) + { + nm->connectivity_cb.cb(nm->connectivity_cb.data, disconnected); + } + nm->mutex->unlock(nm->mutex); +} + +METHOD(network_manager_t, add_connectivity_cb, void, + private_network_manager_t *this, connectivity_cb_t cb, void *data) +{ + this->mutex->lock(this->mutex); + if (!this->connectivity_cb.cb) + { + JNIEnv *env; + jmethodID method_id; + + androidjni_attach_thread(&env); + method_id = (*env)->GetMethodID(env, this->cls, "Register", "()V"); + if (!method_id) + { + androidjni_exception_occurred(env); + } + else + { + (*env)->CallVoidMethod(env, this->obj, method_id); + if (!androidjni_exception_occurred(env)) + { + this->connectivity_cb.cb = cb; + this->connectivity_cb.data = data; + } + androidjni_detach_thread(); + } + } + this->mutex->unlock(this->mutex); +} + +/** + * Unregister the NetworkManager via JNI. + * + * this->mutex has to be locked + */ +static void unregister_network_manager(private_network_manager_t *this) +{ + JNIEnv *env; + jmethodID method_id; + + androidjni_attach_thread(&env); + method_id = (*env)->GetMethodID(env, this->cls, "Unregister", "()V"); + if (!method_id) + { + androidjni_exception_occurred(env); + } + else + { + (*env)->CallVoidMethod(env, this->obj, method_id); + androidjni_exception_occurred(env); + } + androidjni_detach_thread(); +} + +METHOD(network_manager_t, remove_connectivity_cb, void, + private_network_manager_t *this, connectivity_cb_t cb) +{ + this->mutex->lock(this->mutex); + if (this->connectivity_cb.cb == cb) + { + this->connectivity_cb.cb = NULL; + unregister_network_manager(this); + } + this->mutex->unlock(this->mutex); +} + +METHOD(network_manager_t, destroy, void, + private_network_manager_t *this) +{ + JNIEnv *env; + + this->mutex->lock(this->mutex); + if (this->connectivity_cb.cb) + { + this->connectivity_cb.cb = NULL; + unregister_network_manager(this); + } + this->mutex->unlock(this->mutex); + + androidjni_attach_thread(&env); + if (this->obj) + { + (*env)->DeleteGlobalRef(env, this->obj); + } + if (this->cls) + { + (*env)->DeleteGlobalRef(env, this->cls); + } + androidjni_detach_thread(); + this->mutex->destroy(this->mutex); + free(this); +} + +/* + * Described in header. + */ +network_manager_t *network_manager_create(jobject context) +{ + private_network_manager_t *this; + JNIEnv *env; + jmethodID method_id; + jobject obj; + jclass cls; + + INIT(this, + .public = { + .get_local_address = _get_local_address, + .add_connectivity_cb = _add_connectivity_cb, + .remove_connectivity_cb = _remove_connectivity_cb, + .destroy = _destroy, + }, + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + ); + + androidjni_attach_thread(&env); + cls = (*env)->FindClass(env, JNI_PACKAGE_STRING "/NetworkManager"); + if (!cls) + { + goto failed; + } + this->cls = (*env)->NewGlobalRef(env, cls); + method_id = (*env)->GetMethodID(env, cls, "<init>", + "(Landroid/content/Context;)V"); + if (!method_id) + { + goto failed; + } + obj = (*env)->NewObject(env, cls, method_id, context); + if (!obj) + { + goto failed; + } + this->obj = (*env)->NewGlobalRef(env, obj); + androidjni_detach_thread(); + return &this->public; + +failed: + DBG1(DBG_KNL, "failed to build NetworkManager object"); + androidjni_exception_occurred(env); + androidjni_detach_thread(); + destroy(this); + return NULL; +}; diff --git a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h new file mode 100644 index 000000000..634816405 --- /dev/null +++ b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup network_manager network_manager + * @{ @ingroup kernel_android + */ + +#ifndef NETWORK_MANAGER_H_ +#define NETWORK_MANAGER_H_ + +#include <jni.h> + +#include <library.h> +#include <utils/host.h> + +typedef struct network_manager_t network_manager_t; + +/** + * Callback called if connectivity changes somehow. + * + * Implementation should be quick as the call is made by the Java apps main + * thread. + * + * @param data data supplied during registration + * @param disconnected TRUE if currently disconnected + */ +typedef void (*connectivity_cb_t)(void *data, bool disconnected); + +/** + * NetworkManager, used to listen for network changes and retrieve local IP + * addresses. + * + * Communicates with NetworkManager via JNI + */ +struct network_manager_t { + + /** + * Get a local address + * + * @param ipv4 TRUE to get an IPv4 address + * @return the address or NULL if none available + */ + host_t *(*get_local_address)(network_manager_t *this, bool ipv4); + + /** + * Register a callback that is called if connectivity changes + * + * @note Only the first registered callback is currently used + * + * @param cb callback to register + * @param data data provided to callback + */ + void (*add_connectivity_cb)(network_manager_t *this, connectivity_cb_t cb, + void *data); + + /** + * Unregister a previously registered callback for connectivity changes + * + * @param cb previously registered callback + */ + void (*remove_connectivity_cb)(network_manager_t *this, + connectivity_cb_t cb); + + /** + * Destroy a network_manager_t instance + */ + void (*destroy)(network_manager_t *this); +}; + +/** + * Create a network_manager_t instance + * + * @param context Context object + * @return network_manager_t instance + */ +network_manager_t *network_manager_create(jobject context); + +#endif /** NETWORK_MANAGER_H_ @}*/ diff --git a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java index 924781948..02db8c494 100644 --- a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java +++ b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java @@ -18,14 +18,10 @@ package org.strongswan.android.logic; import java.io.File; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; import java.security.PrivateKey; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.Enumeration; import org.strongswan.android.data.VpnProfile; import org.strongswan.android.data.VpnProfileDataSource; @@ -218,9 +214,7 @@ public class CharonVpnService extends VpnService implements Runnable initializeCharon(builder, mLogFile); Log.i(TAG, "charon started"); - String local_address = getLocalIPv4Address(); initiate(mCurrentProfile.getVpnType().getIdentifier(), - local_address != null ? local_address : "0.0.0.0", mCurrentProfile.getGateway(), mCurrentProfile.getUsername(), mCurrentProfile.getPassword()); } @@ -335,15 +329,7 @@ public class CharonVpnService extends VpnService implements Runnable switch (status) { case STATE_CHILD_SA_DOWN: - synchronized (mServiceLock) - { - /* if we are not actively disconnecting we assume the remote terminated - * the connection and call disconnect() to deinitialize charon properly */ - if (mService != null && !mIsDisconnecting) - { - mService.disconnect(); - } - } + /* we ignore this as we use closeaction=restart */ break; case STATE_CHILD_SA_UP: setState(State.CONNECTED); @@ -486,41 +472,7 @@ public class CharonVpnService extends VpnService implements Runnable /** * Initiate VPN, provided by libandroidbridge.so */ - public native void initiate(String type, String local_address, String gateway, - String username, String password); - - /** - * Helper function that retrieves a local IPv4 address. - * - * @return string representation of an IPv4 address, or null if none found - */ - private static String getLocalIPv4Address() - { - try - { - Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); - while (en.hasMoreElements()) - { - NetworkInterface intf = en.nextElement(); - - Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); - while (enumIpAddr.hasMoreElements()) - { - InetAddress inetAddress = enumIpAddr.nextElement(); - if (!inetAddress.isLoopbackAddress() && inetAddress.getAddress().length == 4) - { - return inetAddress.getHostAddress().toString(); - } - } - } - } - catch (SocketException ex) - { - ex.printStackTrace(); - return null; - } - return null; - } + public native void initiate(String type, String gateway, String username, String password); /** * Adapter for VpnService.Builder which is used to access it safely via JNI. @@ -547,7 +499,7 @@ public class CharonVpnService extends VpnService implements Runnable Context context = getApplicationContext(); Intent intent = new Intent(context, MainActivity.class); PendingIntent pending = PendingIntent.getActivity(context, 0, intent, - Intent.FLAG_ACTIVITY_NEW_TASK); + PendingIntent.FLAG_UPDATE_CURRENT); builder.setConfigureIntent(pending); return builder; } diff --git a/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java b/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java new file mode 100644 index 000000000..160865fb7 --- /dev/null +++ b/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +package org.strongswan.android.logic; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +public class NetworkManager extends BroadcastReceiver +{ + private final Context mContext; + private boolean mRegistered; + + public NetworkManager(Context context) + { + mContext = context; + } + + public void Register() + { + mContext.registerReceiver(this, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); + } + + public void Unregister() + { + mContext.unregisterReceiver(this); + } + + @Override + public void onReceive(Context context, Intent intent) + { + ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo info = cm.getActiveNetworkInfo(); + networkChanged(info == null || !info.isConnected()); + } + + /** + * Notify the native parts about a network change + * + * @param disconnected true if no connection is available at the moment + */ + public native void networkChanged(boolean disconnected); + + /** + * Function that retrieves a local address of the given family. + * + * @param ipv4 true to return an IPv4 address, false for IPv6 + * @return string representation of an IPv4 address, or null if none found + */ + public String getLocalAddress(boolean ipv4) + { + try + { + Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); + if (en == null) + { /* no interfaces at all */ + return null; + } + while (en.hasMoreElements()) + { + NetworkInterface intf = en.nextElement(); + if (intf.isLoopback() || !intf.isUp() || + intf.getName().startsWith("tun")) + { + continue; + } + Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); + while (enumIpAddr.hasMoreElements()) + { + InetAddress inetAddress = enumIpAddr.nextElement(); + if (inetAddress.isLoopbackAddress()) + { + continue; + } + if ((ipv4 && inetAddress instanceof Inet4Address) || + (!ipv4 && inetAddress instanceof Inet6Address)) + { + return inetAddress.getHostAddress(); + } + } + } + } + catch (SocketException ex) + { + ex.printStackTrace(); + return null; + } + return null; + } +} |