aboutsummaryrefslogtreecommitdiffstats
path: root/src/frontends/android
diff options
context:
space:
mode:
authorTobias Brunner <tobias@strongswan.org>2015-07-28 13:29:03 +0200
committerTobias Brunner <tobias@strongswan.org>2015-07-28 13:55:23 +0200
commitf14feed01421384b610f896916a28bd4f73f895a (patch)
tree598a92977c36e3af97755049259c8ac6cbece668 /src/frontends/android
parentfbcac07043a6e98ebba8df8bc2c1eb282ae1a2a3 (diff)
parent1e323dc1b772a42470939ab53ad295b3bc786e30 (diff)
downloadstrongswan-f14feed01421384b610f896916a28bd4f73f895a.tar.bz2
strongswan-f14feed01421384b610f896916a28bd4f73f895a.tar.xz
Merge branch 'android-updates'
Fixes the roaming behavior on Android 5+, a linker issue on Android M, a few bugs, and adds several new advanced options for VPN profile (MTU, server port, split tunneling). Also adds methods and a constructor to parse settings_t from a string instead of a file. Fixes #782, #847, #865.
Diffstat (limited to 'src/frontends/android')
-rw-r--r--src/frontends/android/AndroidManifest.xml4
-rw-r--r--src/frontends/android/jni/Android.mk2
-rw-r--r--src/frontends/android/jni/Application.mk1
-rw-r--r--src/frontends/android/jni/libandroidbridge/android_jni.c39
-rw-r--r--src/frontends/android/jni/libandroidbridge/android_jni.h2
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_service.c95
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_service.h12
-rw-r--r--src/frontends/android/jni/libandroidbridge/charonservice.c64
-rw-r--r--src/frontends/android/jni/libandroidbridge/charonservice.h5
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/android_net.c224
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/android_net.h23
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/network_manager.c27
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/network_manager.h9
-rw-r--r--src/frontends/android/project.properties2
-rw-r--r--src/frontends/android/res/layout/profile_detail_view.xml61
-rw-r--r--src/frontends/android/res/values-de/strings.xml10
-rw-r--r--src/frontends/android/res/values-pl/strings.xml8
-rw-r--r--src/frontends/android/res/values-ru/strings.xml8
-rw-r--r--src/frontends/android/res/values-ua/strings.xml8
-rw-r--r--src/frontends/android/res/values/strings.xml10
-rw-r--r--src/frontends/android/src/org/strongswan/android/data/VpnProfile.java38
-rw-r--r--src/frontends/android/src/org/strongswan/android/data/VpnProfileDataSource.java41
-rw-r--r--src/frontends/android/src/org/strongswan/android/data/VpnType.java2
-rw-r--r--src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java167
-rw-r--r--src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java17
-rw-r--r--src/frontends/android/src/org/strongswan/android/ui/ImcStateFragment.java10
-rw-r--r--src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java74
-rw-r--r--src/frontends/android/src/org/strongswan/android/utils/SettingsWriter.java160
28 files changed, 945 insertions, 178 deletions
diff --git a/src/frontends/android/AndroidManifest.xml b/src/frontends/android/AndroidManifest.xml
index 10365985b..5075440e2 100644
--- a/src/frontends/android/AndroidManifest.xml
+++ b/src/frontends/android/AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2012-2014 Tobias Brunner
+ Copyright (C) 2012-2015 Tobias Brunner
Copyright (C) 2012 Giuliano Grassi
Copyright (C) 2012 Ralf Sager
Hochschule fuer Technik Rapperswil
@@ -20,7 +20,7 @@
android:versionCode="26"
android:versionName="1.4.5" >
- <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" />
+ <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="22" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
diff --git a/src/frontends/android/jni/Android.mk b/src/frontends/android/jni/Android.mk
index 670e83de1..1fb233b48 100644
--- a/src/frontends/android/jni/Android.mk
+++ b/src/frontends/android/jni/Android.mk
@@ -6,7 +6,7 @@ include $(CLEAR_VARS)
strongswan_USE_BYOD := true
strongswan_CHARON_PLUGINS := android-log openssl fips-prf random nonce pubkey \
- pkcs1 pkcs8 pem xcbc hmac socket-default kernel-netlink \
+ pkcs1 pkcs8 pem xcbc hmac socket-default \
eap-identity eap-mschapv2 eap-md5 eap-gtc eap-tls
ifneq ($(strongswan_USE_BYOD),)
diff --git a/src/frontends/android/jni/Application.mk b/src/frontends/android/jni/Application.mk
index cdfb47400..9fa668354 100644
--- a/src/frontends/android/jni/Application.mk
+++ b/src/frontends/android/jni/Application.mk
@@ -1,2 +1,3 @@
# select the ABI(s) to build for (see CPU-ARCH-ABIS.html in the NDK docs).
APP_ABI := armeabi x86 mips
+APP_PLATFORM := android-19
diff --git a/src/frontends/android/jni/libandroidbridge/android_jni.c b/src/frontends/android/jni/libandroidbridge/android_jni.c
index 7ab9a24bd..a6412bdf7 100644
--- a/src/frontends/android/jni/libandroidbridge/android_jni.c
+++ b/src/frontends/android/jni/libandroidbridge/android_jni.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -15,6 +15,8 @@
* for more details.
*/
+#include <dlfcn.h>
+
#include "android_jni.h"
#include <library.h>
@@ -25,6 +27,21 @@
*/
static JavaVM *android_jvm;
+static struct {
+ char name[32];
+ void *handle;
+} libs[] = {
+ { "libstrongswan.so", NULL },
+#ifdef USE_BYOD
+ { "libtncif.so", NULL },
+ { "libtnccs.so", NULL },
+ { "libimcv.so", NULL },
+#endif
+ { "libhydra.so", NULL },
+ { "libcharon.so", NULL },
+ { "libipsec.so", NULL },
+};
+
jclass *android_charonvpnservice_class;
jclass *android_charonvpnservice_builder_class;
android_sdk_version_t android_sdk_version;
@@ -79,6 +96,7 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
JNIEnv *env;
jclass jversion;
jfieldID jsdk_int;
+ int i;
android_jvm = vm;
@@ -87,6 +105,15 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
return -1;
}
+ for (i = 0; i < countof(libs); i++)
+ {
+ libs[i].handle = dlopen(libs[i].name, RTLD_GLOBAL);
+ if (!libs[i].handle)
+ {
+ return -1;
+ }
+ }
+
androidjni_threadlocal = thread_value_create(attached_thread_cleanup);
android_charonvpnservice_class =
@@ -109,6 +136,16 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
*/
void JNI_OnUnload(JavaVM *vm, void *reserved)
{
+ int i;
+
androidjni_threadlocal->destroy(androidjni_threadlocal);
+
+ for (i = countof(libs) - 1; i >= 0; i--)
+ {
+ if (libs[i].handle)
+ {
+ dlclose(libs[i].handle);
+ }
+ }
}
diff --git a/src/frontends/android/jni/libandroidbridge/android_jni.h b/src/frontends/android/jni/libandroidbridge/android_jni.h
index 99c0bc2cd..b08670f7e 100644
--- a/src/frontends/android/jni/libandroidbridge/android_jni.h
+++ b/src/frontends/android/jni/libandroidbridge/android_jni.h
@@ -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;
/**
diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c
index 896bb0940..7ef3913f7 100644
--- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c
+++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2014 Tobias Brunner
+ * Copyright (C) 2010-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -32,8 +32,6 @@
typedef struct private_android_service_t private_android_service_t;
-#define TUN_DEFAULT_MTU 1400
-
/**
* private data of Android service
*/
@@ -55,24 +53,9 @@ struct private_android_service_t {
ike_sa_t *ike_sa;
/**
- * the type of VPN
- */
- char *type;
-
- /**
- * gateway
+ * configuration setttings
*/
- char *gateway;
-
- /**
- * username
- */
- char *username;
-
- /**
- * password
- */
- char *password;
+ settings_t *settings;
/**
* lock to safely access the TUN device fd
@@ -85,6 +68,11 @@ struct private_android_service_t {
int tunfd;
/**
+ * MTU of TUN device
+ */
+ int mtu;
+
+ /**
* DNS proxy
*/
android_dns_proxy_t *dns_proxy;
@@ -191,7 +179,7 @@ static job_requeue_t handle_plain(private_android_service_t *this)
return JOB_REQUEUE_DIRECT;
}
- raw = chunk_alloc(TUN_DEFAULT_MTU);
+ raw = chunk_alloc(this->mtu);
len = read(tunfd, raw.ptr, raw.len);
if (len < 0)
{
@@ -309,7 +297,7 @@ static bool setup_tun_device(private_android_service_t *this,
return FALSE;
}
if (!add_routes(builder, child_sa) ||
- !builder->set_mtu(builder, TUN_DEFAULT_MTU))
+ !builder->set_mtu(builder, this->mtu))
{
return FALSE;
}
@@ -621,6 +609,7 @@ static void add_auth_cfg_pw(private_android_service_t *this,
{
identification_t *user;
auth_cfg_t *auth;
+ char *username, *password;
auth = auth_cfg_create();
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
@@ -629,12 +618,14 @@ static void add_auth_cfg_pw(private_android_service_t *this,
auth->add(auth, AUTH_RULE_EAP_TYPE, EAP_TTLS);
}
- user = identification_create_from_string(this->username);
+ username = this->settings->get_str(this->settings, "connection.username",
+ NULL);
+ password = this->settings->get_str(this->settings, "connection.password",
+ NULL);
+ user = identification_create_from_string(username);
auth->add(auth, AUTH_RULE_IDENTITY, user);
- this->creds->add_username_password(this->creds, this->username,
- this->password);
- memwipe(this->password, strlen(this->password));
+ this->creds->add_username_password(this->creds, username, password);
peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
}
@@ -644,6 +635,7 @@ static bool add_auth_cfg_cert(private_android_service_t *this,
certificate_t *cert;
identification_t *id;
auth_cfg_t *auth;
+ char *type;
cert = this->creds->load_user_certificate(this->creds);
if (!cert)
@@ -651,8 +643,9 @@ static bool add_auth_cfg_cert(private_android_service_t *this,
return FALSE;
}
+ type = this->settings->get_str(this->settings, "connection.type", NULL);
auth = auth_cfg_create();
- if (strpfx("ikev2-eap-tls", this->type))
+ if (strpfx("ikev2-eap-tls", type))
{
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
auth->add(auth, AUTH_RULE_EAP_TYPE, EAP_TLS);
@@ -687,11 +680,15 @@ static job_requeue_t initiate(private_android_service_t *this)
.jitter = 300 /* 5min */
}
};
+ char *type, *server;
+ int port;
+ server = this->settings->get_str(this->settings, "connection.server", NULL);
+ port = this->settings->get_int(this->settings, "connection.port",
+ IKEV2_UDP_PORT);
ike_cfg = ike_cfg_create(IKEV2, TRUE, TRUE, "0.0.0.0",
charon->socket->get_port(charon->socket, FALSE),
- this->gateway, IKEV2_UDP_PORT,
- FRAGMENTATION_YES, 0);
+ server, port, FRAGMENTATION_YES, 0);
ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE));
@@ -705,10 +702,11 @@ static job_requeue_t initiate(private_android_service_t *this)
peer_cfg->add_virtual_ip(peer_cfg, host_create_any(AF_INET));
peer_cfg->add_virtual_ip(peer_cfg, host_create_any(AF_INET6));
+ type = this->settings->get_str(this->settings, "connection.type", NULL);
/* local auth config */
- if (streq("ikev2-cert", this->type) ||
- streq("ikev2-cert-eap", this->type) ||
- streq("ikev2-eap-tls", this->type))
+ if (streq("ikev2-cert", type) ||
+ streq("ikev2-cert-eap", type) ||
+ streq("ikev2-eap-tls", type))
{
if (!add_auth_cfg_cert(this, peer_cfg))
{
@@ -718,16 +716,16 @@ static job_requeue_t initiate(private_android_service_t *this)
return JOB_REQUEUE_NONE;
}
}
- if (streq("ikev2-eap", this->type) ||
- streq("ikev2-cert-eap", this->type) ||
- streq("ikev2-byod-eap", this->type))
+ if (streq("ikev2-eap", type) ||
+ streq("ikev2-cert-eap", type) ||
+ streq("ikev2-byod-eap", type))
{
- add_auth_cfg_pw(this, peer_cfg, strpfx(this->type, "ikev2-byod"));
+ add_auth_cfg_pw(this, peer_cfg, strpfx(type, "ikev2-byod"));
}
/* remote auth config */
auth = auth_cfg_create();
- gateway = identification_create_from_string(this->gateway);
+ gateway = identification_create_from_string(server);
auth->add(auth, AUTH_RULE_IDENTITY, gateway);
auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, TRUE);
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
@@ -806,23 +804,15 @@ METHOD(android_service_t, destroy, void,
close_tun_device(this);
this->dns_proxy->destroy(this->dns_proxy);
this->lock->destroy(this->lock);
- free(this->type);
- free(this->gateway);
- free(this->username);
- if (this->password)
- {
- memwipe(this->password, strlen(this->password));
- free(this->password);
- }
+ this->settings->destroy(this->settings);
free(this);
}
/**
* See header
*/
-android_service_t *android_service_create(android_creds_t *creds, char *type,
- char *gateway, char *username,
- char *password)
+android_service_t *android_service_create(android_creds_t *creds,
+ settings_t *settings)
{
private_android_service_t *this;
@@ -840,15 +830,14 @@ android_service_t *android_service_create(android_creds_t *creds, char *type,
},
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
.dns_proxy = android_dns_proxy_create(),
- .username = username,
- .password = password,
- .gateway = gateway,
+ .settings = settings,
.creds = creds,
- .type = type,
.tunfd = -1,
+ .mtu = settings->get_int(settings, "global.mtu", ANDROID_DEFAULT_MTU),
);
/* only allow queries for the VPN gateway */
- this->dns_proxy->add_hostname(this->dns_proxy, gateway);
+ this->dns_proxy->add_hostname(this->dns_proxy,
+ this->settings->get_str(this->settings, "connection.server", NULL));
charon->bus->add_listener(charon->bus, &this->public.listener);
diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.h b/src/frontends/android/jni/libandroidbridge/backend/android_service.h
index 1bfdcf994..1a5175774 100644
--- a/src/frontends/android/jni/libandroidbridge/backend/android_service.h
+++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2012 Tobias Brunner
+ * Copyright (C) 2010-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -52,13 +52,9 @@ struct android_service_t {
* new IKE SA.
*
* @param creds Android specific credential set
- * @param type VPN type (see VpnType.java)
- * @param gateway gateway address
- * @param username user name (local identity)
- * @param password password (if any)
+ * @param settings configuration settings (gets adopted)
*/
-android_service_t *android_service_create(android_creds_t *creds, char *type,
- char *gateway, char *username,
- char *password);
+android_service_t *android_service_create(android_creds_t *creds,
+ settings_t *settings);
#endif /** ANDROID_SERVICE_H_ @}*/
diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.c b/src/frontends/android/jni/libandroidbridge/charonservice.c
index 524630024..2655f7361 100644
--- a/src/frontends/android/jni/libandroidbridge/charonservice.c
+++ b/src/frontends/android/jni/libandroidbridge/charonservice.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -44,7 +44,6 @@
#define ANDROID_RETRASNMIT_TRIES 3
#define ANDROID_RETRANSMIT_TIMEOUT 2.0
#define ANDROID_RETRANSMIT_BASE 1.4
-#define ANDROID_FRAGMENT_SIZE 1400
typedef struct private_charonservice_t private_charonservice_t;
@@ -84,11 +83,6 @@ struct private_charonservice_t {
network_manager_t *network_manager;
/**
- * Handle network events
- */
- android_net_t *net_handler;
-
- /**
* CharonVpnService reference
*/
jobject vpn_service;
@@ -400,18 +394,27 @@ METHOD(charonservice_t, get_network_manager, network_manager_t*,
/**
* Initiate a new connection
*
- * @param gateway gateway address (gets owned)
- * @param username username (gets owned)
- * @param password password (gets owned)
+ * @param settings configuration settings (gets owned)
*/
-static void initiate(char *type, char *gateway, char *username, char *password)
+static void initiate(settings_t *settings)
{
private_charonservice_t *this = (private_charonservice_t*)charonservice;
+ lib->settings->set_str(lib->settings,
+ "charon.plugins.tnc-imc.preferred_language",
+ settings->get_str(settings, "global.language", "en"));
+ /* this is actually the size of the complete IKE/IP packet, so if the MTU
+ * for the TUN devices has to be reduced to pass traffic the IKE packets
+ * will be a bit smaller than necessary as there is no IPsec overhead like
+ * for the tunneled traffic (but compensating that seems like overkill) */
+ lib->settings->set_int(lib->settings,
+ "charon.fragment_size",
+ settings->get_int(settings, "global.mtu",
+ ANDROID_DEFAULT_MTU));
+
this->creds->clear(this->creds);
DESTROY_IF(this->service);
- this->service = android_service_create(this->creds, type, gateway,
- username, password);
+ this->service = android_service_create(this->creds, settings);
}
/**
@@ -423,14 +426,12 @@ static bool charonservice_register(plugin_t *plugin, plugin_feature_t *feature,
private_charonservice_t *this = (private_charonservice_t*)charonservice;
if (reg)
{
- this->net_handler = android_net_create();
lib->credmgr->add_set(lib->credmgr, &this->creds->set);
charon->attributes->add_handler(charon->attributes,
&this->attr->handler);
}
else
{
- this->net_handler->destroy(this->net_handler);
lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
charon->attributes->remove_handler(charon->attributes,
&this->attr->handler);
@@ -466,8 +467,8 @@ static void set_options(char *logfile)
"charon.retransmit_timeout", ANDROID_RETRANSMIT_TIMEOUT);
lib->settings->set_double(lib->settings,
"charon.retransmit_base", ANDROID_RETRANSMIT_BASE);
- lib->settings->set_int(lib->settings,
- "charon.fragment_size", ANDROID_FRAGMENT_SIZE);
+ lib->settings->set_bool(lib->settings,
+ "charon.initiator_only", TRUE);
lib->settings->set_bool(lib->settings,
"charon.close_ike_on_child_failure", TRUE);
/* setting the source address breaks the VpnService.protect() function which
@@ -483,19 +484,6 @@ static void set_options(char *logfile)
* so lets disable IPv6 for now to avoid issues with dual-stack gateways */
lib->settings->set_bool(lib->settings,
"charon.plugins.socket-default.use_ipv6", FALSE);
- /* don't install virtual IPs via kernel-netlink */
- lib->settings->set_bool(lib->settings,
- "charon.install_virtual_ip", FALSE);
- /* kernel-netlink should not trigger roam events, we use Android's
- * ConnectivityManager for that, much less noise */
- lib->settings->set_bool(lib->settings,
- "charon.plugins.kernel-netlink.roam_events", FALSE);
- /* ignore tun devices (it's mostly tun0 but it may already be taken, ignore
- * some others too), also ignore lo as a default route points to it when
- * no connectivity is available */
- lib->settings->set_str(lib->settings,
- "charon.interfaces_ignore", "lo, tun0, tun1, tun2, tun3, "
- "tun4");
#ifdef USE_BYOD
lib->settings->set_str(lib->settings,
@@ -519,6 +507,8 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder,
static plugin_feature_t features[] = {
PLUGIN_CALLBACK(kernel_ipsec_register, kernel_android_ipsec_create),
PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"),
+ PLUGIN_CALLBACK(kernel_net_register, kernel_android_net_create),
+ PLUGIN_PROVIDE(CUSTOM, "kernel-net"),
PLUGIN_CALLBACK(charonservice_register, NULL),
PLUGIN_PROVIDE(CUSTOM, "android-backend"),
PLUGIN_DEPENDS(CUSTOM, "libcharon"),
@@ -705,14 +695,12 @@ JNI_METHOD(CharonVpnService, deinitializeCharon, void)
* Initiate SA
*/
JNI_METHOD(CharonVpnService, initiate, void,
- jstring jtype, jstring jgateway, jstring jusername, jstring jpassword)
+ jstring jconfig)
{
- char *type, *gateway, *username, *password;
-
- type = androidjni_convert_jstring(env, jtype);
- gateway = androidjni_convert_jstring(env, jgateway);
- username = androidjni_convert_jstring(env, jusername);
- password = androidjni_convert_jstring(env, jpassword);
+ settings_t *settings;
+ char *config;
- initiate(type, gateway, username, password);
+ config = androidjni_convert_jstring(env, jconfig);
+ settings = settings_create_string(config);
+ initiate(settings);
}
diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.h b/src/frontends/android/jni/libandroidbridge/charonservice.h
index 0c71d876d..8cb68e099 100644
--- a/src/frontends/android/jni/libandroidbridge/charonservice.h
+++ b/src/frontends/android/jni/libandroidbridge/charonservice.h
@@ -45,6 +45,11 @@ typedef enum android_imc_state_t android_imc_state_t;
typedef struct charonservice_t charonservice_t;
/**
+ * Default value for the MTU of TUN device and the size of IKE fragments
+ */
+#define ANDROID_DEFAULT_MTU 1400
+
+/**
* VPN status codes. As defined in CharonVpnService.java
*/
enum android_vpn_state_t {
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c
index 653e2738b..2ce1bdfac 100644
--- a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c
+++ b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -11,9 +11,14 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <errno.h>
#include "android_net.h"
+#include "../android_jni.h"
#include "../charonservice.h"
#include <hydra.h>
#include <processing/jobs/callback_job.h>
@@ -21,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;
@@ -29,7 +35,7 @@ struct private_android_net_t {
/**
* Public kernel interface
*/
- android_net_t public;
+ kernel_net_t public;
/**
* Reference to NetworkManager object
@@ -37,14 +43,29 @@ struct private_android_net_t {
network_manager_t *network_manager;
/**
- * earliest time of the next roam event
+ * Earliest time of the next roam event
*/
timeval_t next_roam;
/**
- * mutex to check and update roam event time
+ * Mutex to check and update roam event time, and other private members
*/
mutex_t *mutex;
+
+ /**
+ * List of virtual IPs
+ */
+ linked_list_t *vips;
+
+ /**
+ * Socket used to determine source address
+ */
+ int socket_v4;
+
+ /**
+ * Whether the device is currently connected
+ */
+ bool connected;
};
/**
@@ -69,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);
@@ -83,32 +105,210 @@ static void connectivity_cb(private_android_net_t *this,
lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY);
}
-METHOD(android_net_t, destroy, void,
+METHOD(kernel_net_t, get_source_addr, host_t*,
+ private_android_net_t *this, host_t *dest, host_t *src)
+{
+ union {
+ struct sockaddr sockaddr;
+ struct sockaddr_in sin;
+ 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;
+ if (connect(this->socket_v4, &addr.sockaddr, addrlen) < 0)
+ {
+ 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) */
+ if (errno != ENETUNREACH)
+ {
+ 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)
+ {
+ DBG1(DBG_KNL, "failed to determine src address: %s", strerror(errno));
+ return NULL;
+ }
+ 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)
+{
+ return NULL;
+}
+
+METHOD(kernel_net_t, get_interface, bool,
+ private_android_net_t *this, host_t *host, char **name)
+{
+ if (name)
+ { /* the actual name does not matter in our case */
+ *name = strdup("strongswan");
+ }
+ return TRUE;
+}
+
+METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
+ private_android_net_t *this, kernel_address_type_t which)
+{
+ /* return virtual IPs if requested, nothing otherwise */
+ if (which & ADDR_TYPE_VIRTUAL)
+ {
+ this->mutex->lock(this->mutex);
+ return enumerator_create_cleaner(
+ this->vips->create_enumerator(this->vips),
+ (void*)this->mutex->unlock, this->mutex);
+ }
+ return enumerator_create_empty();
+}
+
+METHOD(kernel_net_t, add_ip, status_t,
+ private_android_net_t *this, host_t *virtual_ip, int prefix, char *iface)
+{
+ this->mutex->lock(this->mutex);
+ this->vips->insert_last(this->vips, virtual_ip->clone(virtual_ip));
+ this->mutex->unlock(this->mutex);
+ return SUCCESS;
+}
+
+METHOD(kernel_net_t, del_ip, status_t,
+ private_android_net_t *this, host_t *virtual_ip, int prefix, bool wait)
+{
+ host_t *vip;
+
+ this->mutex->lock(this->mutex);
+ if (this->vips->find_first(this->vips, (void*)virtual_ip->ip_equals,
+ (void**)&vip, virtual_ip) == SUCCESS)
+ {
+ this->vips->remove(this->vips, vip, NULL);
+ vip->destroy(vip);
+ }
+ this->mutex->unlock(this->mutex);
+ return SUCCESS;
+}
+
+METHOD(kernel_net_t, add_route, status_t,
+ private_android_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
+ host_t *gateway, host_t *src_ip, char *if_name)
+{
+ return NOT_SUPPORTED;
+}
+
+METHOD(kernel_net_t, del_route, status_t,
+ private_android_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
+ host_t *gateway, host_t *src_ip, char *if_name)
+{
+ return NOT_SUPPORTED;
+}
+
+METHOD(kernel_net_t, destroy, void,
private_android_net_t *this)
{
this->network_manager->remove_connectivity_cb(this->network_manager,
(void*)connectivity_cb);
this->mutex->destroy(this->mutex);
+ this->vips->destroy(this->vips);
+ close(this->socket_v4);
free(this);
}
-/*
- * Described in header.
- */
-android_net_t *android_net_create()
+kernel_net_t *kernel_android_net_create()
{
private_android_net_t *this;
INIT(this,
.public = {
+ .get_source_addr = _get_source_addr,
+ .get_nexthop = _get_nexthop,
+ .get_interface = _get_interface,
+ .create_address_enumerator = _create_address_enumerator,
+ .add_ip = _add_ip,
+ .del_ip = _del_ip,
+ .add_route = _add_route,
+ .del_route = _del_route,
.destroy = _destroy,
},
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ .vips = linked_list_create(),
.network_manager = charonservice->get_network_manager(charonservice),
);
timerclear(&this->next_roam);
- this->network_manager->add_connectivity_cb(this->network_manager,
- (void*)connectivity_cb, this);
+ 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)
+ {
+ DBG1(DBG_KNL, "failed to create socket to lookup src addresses: %s",
+ strerror(errno));
+ }
+ charonservice->bypass_socket(charonservice, this->socket_v4, AF_INET);
+
+ 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;
-};
+}
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_net.h b/src/frontends/android/jni/libandroidbridge/kernel/android_net.h
index ade83f32a..761fa21bc 100644
--- a/src/frontends/android/jni/libandroidbridge/kernel/android_net.h
+++ b/src/frontends/android/jni/libandroidbridge/kernel/android_net.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -22,25 +22,14 @@
#define ANDROID_NET_H_
#include <library.h>
-
-typedef struct android_net_t android_net_t;
-
-/**
- * Handle connectivity events from NetworkManager
- */
-struct android_net_t {
-
- /**
- * Destroy an android_net_t instance.
- */
- void (*destroy)(android_net_t *this);
-};
+#include <kernel/kernel_net.h>
/**
- * Create an android_net_t instance.
+ * Create an Android-specific kernel_net_t instance.
*
- * @return android_net_t instance
+ * @return kernel_net_t instance
*/
-android_net_t *android_net_create();
+kernel_net_t *kernel_android_net_create();
+
#endif /** ANDROID_NET_H_ @}*/
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c
index f8e560b56..372b25c55 100644
--- a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c
+++ b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -90,8 +90,8 @@ METHOD(network_manager_t, add_connectivity_cb, void,
this->connectivity_cb.cb = cb;
this->connectivity_cb.data = data;
}
- androidjni_detach_thread();
}
+ androidjni_detach_thread();
}
this->mutex->unlock(this->mutex);
}
@@ -132,6 +132,28 @@ METHOD(network_manager_t, remove_connectivity_cb, void,
this->mutex->unlock(this->mutex);
}
+METHOD(network_manager_t, is_connected, bool,
+ private_network_manager_t *this)
+{
+ JNIEnv *env;
+ jmethodID method_id;
+ bool connected = FALSE;
+
+ androidjni_attach_thread(&env);
+ method_id = (*env)->GetMethodID(env, this->cls, "isConnected", "()Z");
+ if (!method_id)
+ {
+ androidjni_exception_occurred(env);
+ }
+ else
+ {
+ connected = (*env)->CallBooleanMethod(env, this->obj, method_id);
+ connected = !androidjni_exception_occurred(env) && connected;
+ }
+ androidjni_detach_thread();
+ return connected;
+}
+
METHOD(network_manager_t, destroy, void,
private_network_manager_t *this)
{
@@ -174,6 +196,7 @@ network_manager_t *network_manager_create(jobject context)
.public = {
.add_connectivity_cb = _add_connectivity_cb,
.remove_connectivity_cb = _remove_connectivity_cb,
+ .is_connected = _is_connected,
.destroy = _destroy,
},
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h
index abca239ea..9a6a715b7 100644
--- a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h
+++ b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -66,6 +66,13 @@ struct network_manager_t {
connectivity_cb_t cb);
/**
+ * Check whether we currently have connectivity
+ *
+ * @return TRUE if currently connected
+ */
+ bool (*is_connected)(network_manager_t *this);
+
+ /**
* Destroy a network_manager_t instance
*/
void (*destroy)(network_manager_t *this);
diff --git a/src/frontends/android/project.properties b/src/frontends/android/project.properties
index a5578ba09..bbe203c84 100644
--- a/src/frontends/android/project.properties
+++ b/src/frontends/android/project.properties
@@ -8,4 +8,4 @@
# project structure.
# Project target.
-target=android-19
+target=android-21
diff --git a/src/frontends/android/res/layout/profile_detail_view.xml b/src/frontends/android/res/layout/profile_detail_view.xml
index 91cd345ff..57d5606ff 100644
--- a/src/frontends/android/res/layout/profile_detail_view.xml
+++ b/src/frontends/android/res/layout/profile_detail_view.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2012 Tobias Brunner
+ Copyright (C) 2012-2015 Tobias Brunner
Copyright (C) 2012 Giuliano Grassi
Copyright (C) 2012 Ralf Sager
Hochschule fuer Technik Rapperswil
@@ -139,6 +139,65 @@
android:id="@+id/select_certificate"
layout="@layout/two_line_button" />
+ <CheckBox
+ android:id="@+id/show_advanced"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/profile_show_advanced_label" />
+
+ <LinearLayout
+ android:id="@+id/advanced_settings"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:text="@string/profile_mtu_label" />
+
+ <EditText
+ android:id="@+id/mtu"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:inputType="number|textNoSuggestions"
+ android:hint="@string/profile_use_default_hint" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:text="@string/profile_port_label" />
+
+ <EditText
+ android:id="@+id/port"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:inputType="number|textNoSuggestions"
+ android:hint="@string/profile_use_default_hint" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:text="@string/profile_split_tunneling_label" />
+
+ <CheckBox
+ android:id="@+id/split_tunneling_v4"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/profile_split_tunnelingv4_title" />
+
+ <CheckBox
+ android:id="@+id/split_tunneling_v6"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/profile_split_tunnelingv6_title" />
+
+ </LinearLayout>
</LinearLayout>
</ScrollView> \ No newline at end of file
diff --git a/src/frontends/android/res/values-de/strings.xml b/src/frontends/android/res/values-de/strings.xml
index 491fe8a8a..6cd5ba50a 100644
--- a/src/frontends/android/res/values-de/strings.xml
+++ b/src/frontends/android/res/values-de/strings.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2012-2013 Tobias Brunner
+ Copyright (C) 2012-2015 Tobias Brunner
Copyright (C) 2012 Giuliano Grassi
Copyright (C) 2012 Ralf Sager
Hochschule fuer Technik Rapperswil
@@ -63,11 +63,19 @@
<string name="profile_ca_auto_label">Automatisch wählen</string>
<string name="profile_ca_select_certificate_label">CA-Zertifikat auswählen</string>
<string name="profile_ca_select_certificate">Wählen Sie ein bestimmtes CA-Zertifikat</string>
+ <string name="profile_show_advanced_label">Erweiterte Einstellungen anzeigen</string>
+ <string name="profile_mtu_label">MTU:</string>
+ <string name="profile_port_label">Server Port:</string>
+ <string name="profile_use_default_hint">(Standardwert verwenden)</string>
+ <string name="profile_split_tunneling_label">Split-Tunneling:</string>
+ <string name="profile_split_tunnelingv4_title">Blockiere IPv4 Verkehr der nicht für das VPN bestimmt ist</string>
+ <string name="profile_split_tunnelingv6_title">Blockiere IPv6 Verkehr der nicht für das VPN bestimmt ist</string>
<!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Bitte geben Sie hier die Gateway-Adresse ein</string>
<string name="alert_text_no_input_username">Bitte geben Sie hier Ihren Benutzernamen ein</string>
<string name="alert_text_nocertfound_title">Kein CA-Zertifikat ausgewählt</string>
<string name="alert_text_nocertfound">Bitte wählen Sie eines aus oder aktivieren Sie <i>Automatisch wählen</i></string>
+ <string name="alert_text_out_of_range">Bitte geben Sie eine Nummer von %1$d - %2$d ein</string>
<string name="tnc_notice_title">EAP-TNC kann Ihre Privatsphäre beeinträchtigen</string>
<string name="tnc_notice_subtitle">Gerätedaten werden an den Gateway-Betreiber gesendet</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) erlaubt Gateway-Betreibern den Gesundheitszustand von Endgeräten zu prüfen.&lt;/p>&lt;p>Dazu kann der Betreiber Daten verlangen, wie etwa eine eindeutige Identifikationsnummer, eine Liste der installierten Pakete, Systemeinstellungen oder kryptografische Prüfsummen von Dateien.&lt;/p>&lt;b>Solche Daten werden nur übermittelt nachdem die Identität des Gateways geprüft wurde.&lt;/b></string>
diff --git a/src/frontends/android/res/values-pl/strings.xml b/src/frontends/android/res/values-pl/strings.xml
index d0cfa48f1..fb2aba003 100644
--- a/src/frontends/android/res/values-pl/strings.xml
+++ b/src/frontends/android/res/values-pl/strings.xml
@@ -63,11 +63,19 @@
<string name="profile_ca_auto_label">Wybierz automatycznie</string>
<string name="profile_ca_select_certificate_label">Wybierz certyfikat CA</string>
<string name="profile_ca_select_certificate">Wybierz określony certyfikat CA</string>
+ <string name="profile_show_advanced_label">Show advanced settings</string>
+ <string name="profile_mtu_label">MTU:</string>
+ <string name="profile_port_label">Server port:</string>
+ <string name="profile_use_default_hint">(use default)</string>
+ <string name="profile_split_tunneling_label">Split tunneling:</string>
+ <string name="profile_split_tunnelingv4_title">Block IPv4 traffic not destined for the VPN</string>
+ <string name="profile_split_tunnelingv6_title">Block IPv6 traffic not destined for the VPN</string>
<!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Wprowadź adres bramki</string>
<string name="alert_text_no_input_username">Wprowadź swoją nazwę użytkownika</string>
<string name="alert_text_nocertfound_title">Nie wybrano żadnego certyfikatu CA</string>
<string name="alert_text_nocertfound">Wybierz lub uaktywnij jeden <i>Wybierz automatycznie</i></string>
+ <string name="alert_text_out_of_range">Please enter a number in the range from %1$d - %2$d</string>
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
<string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
diff --git a/src/frontends/android/res/values-ru/strings.xml b/src/frontends/android/res/values-ru/strings.xml
index eb69183db..eabfc084b 100644
--- a/src/frontends/android/res/values-ru/strings.xml
+++ b/src/frontends/android/res/values-ru/strings.xml
@@ -60,11 +60,19 @@
<string name="profile_ca_auto_label">Выбрать автоматически</string>
<string name="profile_ca_select_certificate_label">Выбрать сертификат CA</string>
<string name="profile_ca_select_certificate">Выбрать CA сертификат</string>
+ <string name="profile_show_advanced_label">Show advanced settings</string>
+ <string name="profile_mtu_label">MTU:</string>
+ <string name="profile_port_label">Server port:</string>
+ <string name="profile_use_default_hint">(use default)</string>
+ <string name="profile_split_tunneling_label">Split tunneling:</string>
+ <string name="profile_split_tunnelingv4_title">Block IPv4 traffic not destined for the VPN</string>
+ <string name="profile_split_tunnelingv6_title">Block IPv6 traffic not destined for the VPN</string>
<!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Пожалуйста введите адрес шлюза</string>
<string name="alert_text_no_input_username">Пожалуйста введите имя пользователя</string>
<string name="alert_text_nocertfound_title">Не выбран сертификат CA</string>
<string name="alert_text_nocertfound">Пожалуйста выберите один <i>Выбрать автоматически</i></string>
+ <string name="alert_text_out_of_range">Please enter a number in the range from %1$d - %2$d</string>
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
<string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
diff --git a/src/frontends/android/res/values-ua/strings.xml b/src/frontends/android/res/values-ua/strings.xml
index e23b9b9b2..d7c238370 100644
--- a/src/frontends/android/res/values-ua/strings.xml
+++ b/src/frontends/android/res/values-ua/strings.xml
@@ -61,11 +61,19 @@
<string name="profile_ca_auto_label">Вибрати автоматично</string>
<string name="profile_ca_select_certificate_label">Вибрати сертифікат CA</string>
<string name="profile_ca_select_certificate">Вибрати спеціальний сертифікат CA</string>
+ <string name="profile_show_advanced_label">Show advanced settings</string>
+ <string name="profile_mtu_label">MTU:</string>
+ <string name="profile_port_label">Server port:</string>
+ <string name="profile_use_default_hint">(use default)</string>
+ <string name="profile_split_tunneling_label">Split tunneling:</string>
+ <string name="profile_split_tunnelingv4_title">Block IPv4 traffic not destined for the VPN</string>
+ <string name="profile_split_tunnelingv6_title">Block IPv6 traffic not destined for the VPN</string>
<!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Введіть адресу шлюза тут</string>
<string name="alert_text_no_input_username">Введіть ім\'я користувача тут</string>
<string name="alert_text_nocertfound_title">Не вибрано сертифікат CA</string>
<string name="alert_text_nocertfound">Будь ласка виберіть один <i>Вибрати автоматично</i></string>
+ <string name="alert_text_out_of_range">Please enter a number in the range from %1$d - %2$d</string>
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
<string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
diff --git a/src/frontends/android/res/values/strings.xml b/src/frontends/android/res/values/strings.xml
index 933a80aff..5c8ebab57 100644
--- a/src/frontends/android/res/values/strings.xml
+++ b/src/frontends/android/res/values/strings.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2012-2013 Tobias Brunner
+ Copyright (C) 2012-2015 Tobias Brunner
Copyright (C) 2012 Giuliano Grassi
Copyright (C) 2012 Ralf Sager
Hochschule fuer Technik Rapperswil
@@ -63,11 +63,19 @@
<string name="profile_ca_auto_label">Select automatically</string>
<string name="profile_ca_select_certificate_label">Select CA certificate</string>
<string name="profile_ca_select_certificate">Select a specific CA certificate</string>
+ <string name="profile_show_advanced_label">Show advanced settings</string>
+ <string name="profile_mtu_label">MTU:</string>
+ <string name="profile_port_label">Server port:</string>
+ <string name="profile_use_default_hint">(use default)</string>
+ <string name="profile_split_tunneling_label">Split tunneling:</string>
+ <string name="profile_split_tunnelingv4_title">Block IPv4 traffic not destined for the VPN</string>
+ <string name="profile_split_tunnelingv6_title">Block IPv6 traffic not destined for the VPN</string>
<!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Please enter the gateway address here</string>
<string name="alert_text_no_input_username">Please enter your username here</string>
<string name="alert_text_nocertfound_title">No CA certificate selected</string>
<string name="alert_text_nocertfound">Please select one or activate <i>Select automatically</i></string>
+ <string name="alert_text_out_of_range">Please enter a number in the range from %1$d - %2$d</string>
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
<string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
diff --git a/src/frontends/android/src/org/strongswan/android/data/VpnProfile.java b/src/frontends/android/src/org/strongswan/android/data/VpnProfile.java
index 8323826d2..5c64ad0e5 100644
--- a/src/frontends/android/src/org/strongswan/android/data/VpnProfile.java
+++ b/src/frontends/android/src/org/strongswan/android/data/VpnProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -17,9 +17,15 @@
package org.strongswan.android.data;
+
public class VpnProfile implements Cloneable
{
+ /* While storing this as EnumSet would be nicer this simplifies storing it in a database */
+ public static final int SPLIT_TUNNELING_BLOCK_IPV4 = 1;
+ public static final int SPLIT_TUNNELING_BLOCK_IPV6 = 2;
+
private String mName, mGateway, mUsername, mPassword, mCertificate, mUserCertificate;
+ private Integer mMTU, mPort, mSplitTunneling;
private VpnType mVpnType;
private long mId = -1;
@@ -103,6 +109,36 @@ public class VpnProfile implements Cloneable
this.mUserCertificate = alias;
}
+ public Integer getMTU()
+ {
+ return mMTU;
+ }
+
+ public void setMTU(Integer mtu)
+ {
+ this.mMTU = mtu;
+ }
+
+ public Integer getPort()
+ {
+ return mPort;
+ }
+
+ public void setPort(Integer port)
+ {
+ this.mPort = port;
+ }
+
+ public Integer getSplitTunneling()
+ {
+ return mSplitTunneling;
+ }
+
+ public void setSplitTunneling(Integer splitTunneling)
+ {
+ this.mSplitTunneling = splitTunneling;
+ }
+
@Override
public String toString()
{
diff --git a/src/frontends/android/src/org/strongswan/android/data/VpnProfileDataSource.java b/src/frontends/android/src/org/strongswan/android/data/VpnProfileDataSource.java
index 6fd68d0c8..45e9b8650 100644
--- a/src/frontends/android/src/org/strongswan/android/data/VpnProfileDataSource.java
+++ b/src/frontends/android/src/org/strongswan/android/data/VpnProfileDataSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -40,6 +40,9 @@ public class VpnProfileDataSource
public static final String KEY_PASSWORD = "password";
public static final String KEY_CERTIFICATE = "certificate";
public static final String KEY_USER_CERTIFICATE = "user_certificate";
+ public static final String KEY_MTU = "mtu";
+ public static final String KEY_PORT = "port";
+ public static final String KEY_SPLIT_TUNNELING = "split_tunneling";
private DatabaseHelper mDbHelper;
private SQLiteDatabase mDatabase;
@@ -48,7 +51,7 @@ public class VpnProfileDataSource
private static final String DATABASE_NAME = "strongswan.db";
private static final String TABLE_VPNPROFILE = "vpnprofile";
- private static final int DATABASE_VERSION = 4;
+ private static final int DATABASE_VERSION = 7;
public static final String DATABASE_CREATE =
"CREATE TABLE " + TABLE_VPNPROFILE + " (" +
@@ -59,7 +62,10 @@ public class VpnProfileDataSource
KEY_USERNAME + " TEXT," +
KEY_PASSWORD + " TEXT," +
KEY_CERTIFICATE + " TEXT," +
- KEY_USER_CERTIFICATE + " TEXT" +
+ KEY_USER_CERTIFICATE + " TEXT," +
+ KEY_MTU + " INTEGER," +
+ KEY_PORT + " INTEGER," +
+ KEY_SPLIT_TUNNELING + " INTEGER" +
");";
private static final String[] ALL_COLUMNS = new String[] {
KEY_ID,
@@ -70,6 +76,9 @@ public class VpnProfileDataSource
KEY_PASSWORD,
KEY_CERTIFICATE,
KEY_USER_CERTIFICATE,
+ KEY_MTU,
+ KEY_PORT,
+ KEY_SPLIT_TUNNELING,
};
private static class DatabaseHelper extends SQLiteOpenHelper
@@ -104,6 +113,21 @@ public class VpnProfileDataSource
{ /* remove NOT NULL constraint from username column */
updateColumns(db);
}
+ if (oldVersion < 5)
+ {
+ db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_MTU +
+ " INTEGER;");
+ }
+ if (oldVersion < 6)
+ {
+ db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_PORT +
+ " INTEGER;");
+ }
+ if (oldVersion < 7)
+ {
+ db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_SPLIT_TUNNELING +
+ " INTEGER;");
+ }
}
private void updateColumns(SQLiteDatabase db)
@@ -255,6 +279,9 @@ public class VpnProfileDataSource
profile.setPassword(cursor.getString(cursor.getColumnIndex(KEY_PASSWORD)));
profile.setCertificateAlias(cursor.getString(cursor.getColumnIndex(KEY_CERTIFICATE)));
profile.setUserCertificateAlias(cursor.getString(cursor.getColumnIndex(KEY_USER_CERTIFICATE)));
+ profile.setMTU(getInt(cursor, cursor.getColumnIndex(KEY_MTU)));
+ profile.setPort(getInt(cursor, cursor.getColumnIndex(KEY_PORT)));
+ profile.setSplitTunneling(getInt(cursor, cursor.getColumnIndex(KEY_SPLIT_TUNNELING)));
return profile;
}
@@ -268,6 +295,14 @@ public class VpnProfileDataSource
values.put(KEY_PASSWORD, profile.getPassword());
values.put(KEY_CERTIFICATE, profile.getCertificateAlias());
values.put(KEY_USER_CERTIFICATE, profile.getUserCertificateAlias());
+ values.put(KEY_MTU, profile.getMTU());
+ values.put(KEY_PORT, profile.getPort());
+ values.put(KEY_SPLIT_TUNNELING, profile.getSplitTunneling());
return values;
}
+
+ private Integer getInt(Cursor cursor, int columnIndex)
+ {
+ return cursor.isNull(columnIndex) ? null : cursor.getInt(columnIndex);
+ }
}
diff --git a/src/frontends/android/src/org/strongswan/android/data/VpnType.java b/src/frontends/android/src/org/strongswan/android/data/VpnType.java
index bffa8384c..bb7fd09f3 100644
--- a/src/frontends/android/src/org/strongswan/android/data/VpnType.java
+++ b/src/frontends/android/src/org/strongswan/android/data/VpnType.java
@@ -24,7 +24,7 @@ public enum VpnType
IKEV2_CERT("ikev2-cert", EnumSet.of(VpnTypeFeature.CERTIFICATE)),
IKEV2_CERT_EAP("ikev2-cert-eap", EnumSet.of(VpnTypeFeature.USER_PASS, VpnTypeFeature.CERTIFICATE)),
IKEV2_EAP_TLS("ikev2-eap-tls", EnumSet.of(VpnTypeFeature.CERTIFICATE)),
- IKEV2_BYOD_EAP("ikev2-byod-eap", EnumSet.of(VpnTypeFeature.USER_PASS, VpnTypeFeature.CERTIFICATE, VpnTypeFeature.BYOD));
+ IKEV2_BYOD_EAP("ikev2-byod-eap", EnumSet.of(VpnTypeFeature.USER_PASS, VpnTypeFeature.BYOD));
/**
* Features of a VPN type.
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 7cdaee735..e5241d5a7 100644
--- a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java
+++ b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -18,11 +18,16 @@
package org.strongswan.android.logic;
import java.io.File;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import org.strongswan.android.data.VpnProfile;
import org.strongswan.android.data.VpnProfileDataSource;
@@ -32,7 +37,9 @@ import org.strongswan.android.logic.VpnStateService.State;
import org.strongswan.android.logic.imc.ImcState;
import org.strongswan.android.logic.imc.RemediationInstruction;
import org.strongswan.android.ui.MainActivity;
+import org.strongswan.android.utils.SettingsWriter;
+import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
@@ -40,11 +47,13 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.VpnService;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.security.KeyChain;
import android.security.KeyChainException;
+import android.system.OsConstants;
import android.util.Log;
public class CharonVpnService extends VpnService implements Runnable
@@ -211,13 +220,19 @@ public class CharonVpnService extends VpnService implements Runnable
startConnection(mCurrentProfile);
mIsDisconnecting = false;
- BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName());
+ BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName(), mCurrentProfile.getSplitTunneling());
if (initializeCharon(builder, mLogFile, mCurrentProfile.getVpnType().has(VpnTypeFeature.BYOD)))
{
Log.i(TAG, "charon started");
- initiate(mCurrentProfile.getVpnType().getIdentifier(),
- mCurrentProfile.getGateway(), mCurrentProfile.getUsername(),
- mCurrentProfile.getPassword());
+ SettingsWriter writer = new SettingsWriter();
+ writer.setValue("global.language", Locale.getDefault().getLanguage());
+ writer.setValue("global.mtu", mCurrentProfile.getMTU());
+ writer.setValue("connection.type", mCurrentProfile.getVpnType().getIdentifier());
+ writer.setValue("connection.server", mCurrentProfile.getGateway());
+ writer.setValue("connection.port", mCurrentProfile.getPort());
+ writer.setValue("connection.username", mCurrentProfile.getUsername());
+ writer.setValue("connection.password", mCurrentProfile.getPassword());
+ initiate(writer.serialize());
}
else
{
@@ -497,7 +512,6 @@ public class CharonVpnService extends VpnService implements Runnable
private PrivateKey getUserKey() throws KeyChainException, InterruptedException
{
return KeyChain.getPrivateKey(getApplicationContext(), mCurrentUserCertificateAlias);
-
}
/**
@@ -518,7 +532,7 @@ public class CharonVpnService extends VpnService implements Runnable
/**
* Initiate VPN, provided by libandroidbridge.so
*/
- public native void initiate(String type, String gateway, String username, String password);
+ public native void initiate(String config);
/**
* Adapter for VpnService.Builder which is used to access it safely via JNI.
@@ -527,15 +541,17 @@ public class CharonVpnService extends VpnService implements Runnable
public class BuilderAdapter
{
private final String mName;
+ private final Integer mSplitTunneling;
private VpnService.Builder mBuilder;
private BuilderCache mCache;
private BuilderCache mEstablishedCache;
- public BuilderAdapter(String name)
+ public BuilderAdapter(String name, Integer splitTunneling)
{
mName = name;
+ mSplitTunneling = splitTunneling;
mBuilder = createBuilder(name);
- mCache = new BuilderCache();
+ mCache = new BuilderCache(mSplitTunneling);
}
private VpnService.Builder createBuilder(String name)
@@ -557,7 +573,6 @@ public class CharonVpnService extends VpnService implements Runnable
{
try
{
- mBuilder.addAddress(address, prefixLength);
mCache.addAddress(address, prefixLength);
}
catch (IllegalArgumentException ex)
@@ -572,6 +587,7 @@ public class CharonVpnService extends VpnService implements Runnable
try
{
mBuilder.addDnsServer(address);
+ mCache.recordAddressFamily(address);
}
catch (IllegalArgumentException ex)
{
@@ -584,7 +600,6 @@ public class CharonVpnService extends VpnService implements Runnable
{
try
{
- mBuilder.addRoute(address, prefixLength);
mCache.addRoute(address, prefixLength);
}
catch (IllegalArgumentException ex)
@@ -611,7 +626,6 @@ public class CharonVpnService extends VpnService implements Runnable
{
try
{
- mBuilder.setMtu(mtu);
mCache.setMtu(mtu);
}
catch (IllegalArgumentException ex)
@@ -626,6 +640,7 @@ public class CharonVpnService extends VpnService implements Runnable
ParcelFileDescriptor fd;
try
{
+ mCache.applyData(mBuilder);
fd = mBuilder.establish();
}
catch (Exception ex)
@@ -641,7 +656,7 @@ public class CharonVpnService extends VpnService implements Runnable
* builder anymore, but we might need another when reestablishing */
mBuilder = createBuilder(mName);
mEstablishedCache = mCache;
- mCache = new BuilderCache();
+ mCache = new BuilderCache(mSplitTunneling);
return fd.detachFd();
}
@@ -679,17 +694,40 @@ public class CharonVpnService extends VpnService implements Runnable
public class BuilderCache
{
private final List<PrefixedAddress> mAddresses = new ArrayList<PrefixedAddress>();
- private final List<PrefixedAddress> mRoutes = new ArrayList<PrefixedAddress>();
+ private final List<PrefixedAddress> mRoutesIPv4 = new ArrayList<PrefixedAddress>();
+ private final List<PrefixedAddress> mRoutesIPv6 = new ArrayList<PrefixedAddress>();
+ private final int mSplitTunneling;
private int mMtu;
+ private boolean mIPv4Seen, mIPv6Seen;
+
+ public BuilderCache(Integer splitTunneling)
+ {
+ mSplitTunneling = splitTunneling != null ? splitTunneling : 0;
+ }
public void addAddress(String address, int prefixLength)
{
mAddresses.add(new PrefixedAddress(address, prefixLength));
+ recordAddressFamily(address);
}
public void addRoute(String address, int prefixLength)
{
- mRoutes.add(new PrefixedAddress(address, prefixLength));
+ try
+ {
+ if (isIPv6(address))
+ {
+ mRoutesIPv6.add(new PrefixedAddress(address, prefixLength));
+ }
+ else
+ {
+ mRoutesIPv4.add(new PrefixedAddress(address, prefixLength));
+ }
+ }
+ catch (UnknownHostException ex)
+ {
+ ex.printStackTrace();
+ }
}
public void setMtu(int mtu)
@@ -697,19 +735,89 @@ public class CharonVpnService extends VpnService implements Runnable
mMtu = mtu;
}
+ public void recordAddressFamily(String address)
+ {
+ try
+ {
+ if (isIPv6(address))
+ {
+ mIPv6Seen = true;
+ }
+ else
+ {
+ mIPv4Seen = true;
+ }
+ }
+ catch (UnknownHostException ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void applyData(VpnService.Builder builder)
{
for (PrefixedAddress address : mAddresses)
{
builder.addAddress(address.mAddress, address.mPrefix);
}
- for (PrefixedAddress route : mRoutes)
+ /* add routes depending on whether split tunneling is allowed or not,
+ * that is, whether we have to handle and block non-VPN traffic */
+ if ((mSplitTunneling & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) == 0)
+ {
+ if (mIPv4Seen)
+ { /* split tunneling is used depending on the routes */
+ for (PrefixedAddress route : mRoutesIPv4)
+ {
+ builder.addRoute(route.mAddress, route.mPrefix);
+ }
+ }
+ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ { /* allow traffic that would otherwise be blocked to bypass the VPN */
+ builder.allowFamily(OsConstants.AF_INET);
+ }
+ }
+ else if (mIPv4Seen)
+ { /* only needed if we've seen any addresses. otherwise, traffic
+ * is blocked by default (we also install no routes in that case) */
+ builder.addRoute("0.0.0.0", 0);
+ }
+ /* same thing for IPv6 */
+ if ((mSplitTunneling & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6) == 0)
+ {
+ if (mIPv6Seen)
+ {
+ for (PrefixedAddress route : mRoutesIPv6)
+ {
+ builder.addRoute(route.mAddress, route.mPrefix);
+ }
+ }
+ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ {
+ builder.allowFamily(OsConstants.AF_INET6);
+ }
+ }
+ else if (mIPv6Seen)
{
- builder.addRoute(route.mAddress, route.mPrefix);
+ builder.addRoute("::", 0);
}
builder.setMtu(mMtu);
}
+ private boolean isIPv6(String address) throws UnknownHostException
+ {
+ InetAddress addr = InetAddress.getByName(address);
+ if (addr instanceof Inet4Address)
+ {
+ return false;
+ }
+ else if (addr instanceof Inet6Address)
+ {
+ return true;
+ }
+ return false;
+ }
+
private class PrefixedAddress
{
public String mAddress;
@@ -725,22 +833,25 @@ public class CharonVpnService extends VpnService implements Runnable
/*
* The libraries are extracted to /data/data/org.strongswan.android/...
- * during installation.
+ * during installation. On newer releases most are loaded in JNI_OnLoad.
*/
static
{
- System.loadLibrary("strongswan");
-
- if (MainActivity.USE_BYOD)
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2)
{
- System.loadLibrary("tncif");
- System.loadLibrary("tnccs");
- System.loadLibrary("imcv");
- }
+ System.loadLibrary("strongswan");
- System.loadLibrary("hydra");
- System.loadLibrary("charon");
- System.loadLibrary("ipsec");
+ if (MainActivity.USE_BYOD)
+ {
+ System.loadLibrary("tncif");
+ System.loadLibrary("tnccs");
+ System.loadLibrary("imcv");
+ }
+
+ System.loadLibrary("hydra");
+ System.loadLibrary("charon");
+ System.loadLibrary("ipsec");
+ }
System.loadLibrary("androidbridge");
}
}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java b/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java
index 8ea07f4c0..ebe1d0080 100644
--- a/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java
+++ b/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -42,12 +42,21 @@ public class NetworkManager extends BroadcastReceiver
mContext.unregisterReceiver(this);
}
+ public boolean isConnected()
+ {
+ ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo info = null;
+ if (cm != null)
+ {
+ info = cm.getActiveNetworkInfo();
+ }
+ return info != null && info.isConnected();
+ }
+
@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());
+ networkChanged(!isConnected());
}
/**
diff --git a/src/frontends/android/src/org/strongswan/android/ui/ImcStateFragment.java b/src/frontends/android/src/org/strongswan/android/ui/ImcStateFragment.java
index 39f86b460..5b1799744 100644
--- a/src/frontends/android/src/org/strongswan/android/ui/ImcStateFragment.java
+++ b/src/frontends/android/src/org/strongswan/android/ui/ImcStateFragment.java
@@ -24,6 +24,7 @@ import org.strongswan.android.logic.imc.ImcState;
import org.strongswan.android.logic.imc.RemediationInstruction;
import android.app.Fragment;
+import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.Service;
import android.content.ComponentName;
@@ -82,7 +83,7 @@ public class ImcStateFragment extends Fragment implements VpnStateListener
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
- View view = inflater.inflate(R.layout.imc_state_fragment, null);
+ View view = inflater.inflate(R.layout.imc_state_fragment, container, false);
mButton = (LinearLayout)view.findViewById(R.id.imc_state_button);
mButton.setOnClickListener(new OnClickListener() {
@@ -174,7 +175,12 @@ public class ImcStateFragment extends Fragment implements VpnStateListener
public void updateView()
{
- FragmentTransaction ft = getFragmentManager().beginTransaction();
+ FragmentManager fm = getFragmentManager();
+ if (fm == null)
+ {
+ return;
+ }
+ FragmentTransaction ft = fm.beginTransaction();
switch (mService.getImcState())
{
diff --git a/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java b/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java
index 41cd6e98c..a8b3daa06 100644
--- a/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java
+++ b/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java
@@ -60,6 +60,8 @@ import android.widget.TextView;
public class VpnProfileDetailActivity extends Activity
{
private static final int SELECT_TRUSTED_CERTIFICATE = 0;
+ private static final int MTU_MIN = 1280;
+ private static final int MTU_MAX = 1500;
private VpnProfileDataSource mDataSource;
private Long mId;
@@ -79,6 +81,12 @@ public class VpnProfileDetailActivity extends Activity
private CheckBox mCheckAuto;
private RelativeLayout mSelectCert;
private RelativeLayout mTncNotice;
+ private CheckBox mShowAdvanced;
+ private ViewGroup mAdvancedSettings;
+ private EditText mMTU;
+ private EditText mPort;
+ private CheckBox mBlockIPv4;
+ private CheckBox mBlockIPv6;
@Override
public void onCreate(Bundle savedInstanceState)
@@ -108,6 +116,14 @@ public class VpnProfileDetailActivity extends Activity
mCheckAuto = (CheckBox)findViewById(R.id.ca_auto);
mSelectCert = (RelativeLayout)findViewById(R.id.select_certificate);
+ mShowAdvanced = (CheckBox)findViewById(R.id.show_advanced);
+ mAdvancedSettings = (ViewGroup)findViewById(R.id.advanced_settings);
+
+ mMTU = (EditText)findViewById(R.id.mtu);
+ mPort = (EditText)findViewById(R.id.port);
+ mBlockIPv4 = (CheckBox)findViewById(R.id.split_tunneling_v4);
+ mBlockIPv6 = (CheckBox)findViewById(R.id.split_tunneling_v6);
+
mSelectVpnType.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
@@ -154,6 +170,14 @@ public class VpnProfileDetailActivity extends Activity
}
});
+ mShowAdvanced.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
+ {
+ updateAdvancedSettings();
+ }
+ });
+
mId = savedInstanceState == null ? null : savedInstanceState.getLong(VpnProfileDataSource.KEY_ID);
if (mId == null)
{
@@ -165,6 +189,7 @@ public class VpnProfileDetailActivity extends Activity
updateCredentialView();
updateCertificateSelector();
+ updateAdvancedSettings();
}
@Override
@@ -315,6 +340,22 @@ public class VpnProfileDetailActivity extends Activity
}
/**
+ * Update the advanced settings UI depending on whether any advanced
+ * settings have already been made.
+ */
+ private void updateAdvancedSettings()
+ {
+ boolean show = mShowAdvanced.isChecked();
+ if (!show && mProfile != null)
+ {
+ Integer st = mProfile.getSplitTunneling();
+ show = mProfile.getMTU() != null || mProfile.getPort() != null || (st != null && st != 0);
+ }
+ mShowAdvanced.setVisibility(!show ? View.VISIBLE : View.GONE);
+ mAdvancedSettings.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
+ /**
* Save or update the profile depending on whether we actually have a
* profile object or not (this was created in updateProfileData)
*/
@@ -368,6 +409,18 @@ public class VpnProfileDetailActivity extends Activity
showCertificateAlert();
valid = false;
}
+ Integer mtu = getInteger(mMTU);
+ if (mtu != null && (mtu < MTU_MIN || mtu > MTU_MAX))
+ {
+ mMTU.setError(String.format(getString(R.string.alert_text_out_of_range), MTU_MIN, MTU_MAX));
+ valid = false;
+ }
+ Integer port = getInteger(mPort);
+ if (port != null && (port < 1 || port > 65535))
+ {
+ mPort.setError(String.format(getString(R.string.alert_text_out_of_range), 1, 65535));
+ valid = false;
+ }
return valid;
}
@@ -395,6 +448,12 @@ public class VpnProfileDetailActivity extends Activity
}
String certAlias = mCheckAuto.isChecked() ? null : mCertEntry.getAlias();
mProfile.setCertificateAlias(certAlias);
+ mProfile.setMTU(getInteger(mMTU));
+ mProfile.setPort(getInteger(mPort));
+ int st = 0;
+ st |= mBlockIPv4.isChecked() ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4 : 0;
+ st |= mBlockIPv6.isChecked() ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6 : 0;
+ mProfile.setSplitTunneling(st == 0 ? null : st);
}
/**
@@ -417,6 +476,10 @@ public class VpnProfileDetailActivity extends Activity
mVpnType = mProfile.getVpnType();
mUsername.setText(mProfile.getUsername());
mPassword.setText(mProfile.getPassword());
+ mMTU.setText(mProfile.getMTU() != null ? mProfile.getMTU().toString() : null);
+ mPort.setText(mProfile.getPort() != null ? mProfile.getPort().toString() : null);
+ mBlockIPv4.setChecked(mProfile.getSplitTunneling() != null ? (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) != 0 : false);
+ mBlockIPv6.setChecked(mProfile.getSplitTunneling() != null ? (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6) != 0 : false);
useralias = mProfile.getUserCertificateAlias();
alias = mProfile.getCertificateAlias();
getActionBar().setTitle(mProfile.getName());
@@ -458,6 +521,17 @@ public class VpnProfileDetailActivity extends Activity
}
}
+ /**
+ * Get the integer value in the given text box or null if empty
+ *
+ * @param view text box (numeric entry assumed)
+ */
+ private Integer getInteger(EditText view)
+ {
+ String value = view.getText().toString().trim();
+ return value.isEmpty() ? null : Integer.valueOf(value);
+ }
+
private class SelectUserCertOnClickListener implements OnClickListener, KeyChainAliasCallback
{
@Override
diff --git a/src/frontends/android/src/org/strongswan/android/utils/SettingsWriter.java b/src/frontends/android/src/org/strongswan/android/utils/SettingsWriter.java
new file mode 100644
index 000000000..01c0ab8a8
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/utils/SettingsWriter.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 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.utils;
+
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+
+
+/**
+ * Simple generator for data/files that may be parsed by libstrongswan's
+ * settings_t class.
+ */
+public class SettingsWriter
+{
+ /**
+ * Top-level section
+ */
+ private final SettingsSection mTop = new SettingsSection();
+
+ /**
+ * Set a string value
+ * @param key
+ * @param value
+ * @return the writer
+ */
+ public SettingsWriter setValue(String key, String value)
+ {
+ Pattern pattern = Pattern.compile("[^#{}=\"\\n\\t ]+");
+ if (key == null || !pattern.matcher(key).matches())
+ {
+ return this;
+ }
+ String[] keys = key.split("\\.");
+ SettingsSection section = mTop;
+ section = findOrCreateSection(Arrays.copyOfRange(keys, 0, keys.length-1));
+ section.Settings.put(keys[keys.length-1], value);
+ return this;
+ }
+
+ /**
+ * Set an integer value
+ * @param key
+ * @param value
+ * @return the writer
+ */
+ public SettingsWriter setValue(String key, Integer value)
+ {
+ return setValue(key, value == null ? null : value.toString());
+ }
+
+ /**
+ * Set a boolean value
+ * @param key
+ * @param value
+ * @return the writer
+ */
+ public SettingsWriter setValue(String key, Boolean value)
+ {
+ return setValue(key, value == null ? null : value ? "1" : "0");
+ }
+
+ /**
+ * Serializes the settings to a string in the format understood by
+ * libstrongswan's settings_t parser.
+ * @return serialized settings
+ */
+ public String serialize()
+ {
+ StringBuilder builder = new StringBuilder();
+ serializeSection(mTop, builder);
+ return builder.toString();
+ }
+
+ /**
+ * Serialize the settings in a section and recursively serialize sub-sections
+ * @param section
+ * @param builder
+ */
+ private void serializeSection(SettingsSection section, StringBuilder builder)
+ {
+ for (Entry<String, String> setting : section.Settings.entrySet())
+ {
+ builder.append(setting.getKey()).append('=');
+ if (setting.getValue() != null)
+ {
+ builder.append("\"").append(escapeValue(setting.getValue())).append("\"");
+ }
+ builder.append('\n');
+ }
+
+ for (Entry<String, SettingsSection> subsection : section.Sections.entrySet())
+ {
+ builder.append(subsection.getKey()).append(" {\n");
+ serializeSection(subsection.getValue(), builder);
+ builder.append("}\n");
+ }
+ }
+
+ /**
+ * Escape value so it may be wrapped in "
+ * @param value
+ * @return
+ */
+ private String escapeValue(String value)
+ {
+ return value.replace("\"", "\\\"");
+ }
+
+ /**
+ * Find or create the nested sections with the given names
+ * @param sections list of section names
+ * @return final section
+ */
+ private SettingsSection findOrCreateSection(String[] sections)
+ {
+ SettingsSection section = mTop;
+ for (String name : sections)
+ {
+ SettingsSection subsection = section.Sections.get(name);
+ if (subsection == null)
+ {
+ subsection = new SettingsSection();
+ section.Sections.put(name, subsection);
+ }
+ section = subsection;
+ }
+ return section;
+ }
+
+ /**
+ * A section containing sub-sections and settings.
+ */
+ private class SettingsSection
+ {
+ /**
+ * Assigned key/value pairs
+ */
+ LinkedHashMap<String,String> Settings = new LinkedHashMap<String, String>();
+
+ /**
+ * Assigned sub-sections
+ */
+ LinkedHashMap<String,SettingsSection> Sections = new LinkedHashMap<String, SettingsWriter.SettingsSection>();
+ }
+}