From 5f613590b042e49d5e209f6372b0b3c1d7adb543 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 10 Jun 2015 15:13:49 +0200 Subject: [PATCH 01/32] android: Increase the minSdkVersion to 15 and targetSdkVersion to 22 There are no devices anymore that use API level 14 (4.0-4.0.2) and 22 is the most recent level. --- src/frontends/android/AndroidManifest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 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 @@ Bitte geben Sie hier die Gateway-Adresse ein Bitte geben Sie hier Ihren Benutzernamen ein Kein CA-Zertifikat ausgewählt Bitte wählen Sie eines aus oder aktivieren Sie Automatisch wählen + Bitte geben Sie eine Nummer von %1$d - %2$d ein EAP-TNC kann Ihre Privatsphäre beeinträchtigen Gerätedaten werden an den Gateway-Betreiber gesendet <p>Trusted Network Connect (TNC) erlaubt Gateway-Betreibern den Gesundheitszustand von Endgeräten zu prüfen.</p><p>Dazu kann der Betreiber Daten verlangen, wie etwa eine eindeutige Identifikationsnummer, eine Liste der installierten Pakete, Systemeinstellungen oder kryptografische Prüfsummen von Dateien.</p><b>Solche Daten werden nur übermittelt nachdem die Identität des Gateways geprüft wurde.</b> diff --git a/src/frontends/android/res/values-pl/strings.xml b/src/frontends/android/res/values-pl/strings.xml index d0cfa48f1..a46e8da2a 100644 --- a/src/frontends/android/res/values-pl/strings.xml +++ b/src/frontends/android/res/values-pl/strings.xml @@ -63,11 +63,15 @@ Wybierz automatycznie Wybierz certyfikat CA Wybierz określony certyfikat CA + Show advanced settings + MTU: + (use default) Wprowadź adres bramki Wprowadź swoją nazwę użytkownika Nie wybrano żadnego certyfikatu CA Wybierz lub uaktywnij jeden Wybierz automatycznie + Please enter a number in the range from %1$d - %2$d EAP-TNC may affect your privacy Device data is sent to the gateway operator <p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.</p><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.</p><b>Any data will be sent only after verifying the gateway\'s identity.</b> diff --git a/src/frontends/android/res/values-ru/strings.xml b/src/frontends/android/res/values-ru/strings.xml index eb69183db..7fbe231d3 100644 --- a/src/frontends/android/res/values-ru/strings.xml +++ b/src/frontends/android/res/values-ru/strings.xml @@ -60,11 +60,15 @@ Выбрать автоматически Выбрать сертификат CA Выбрать CA сертификат + Show advanced settings + MTU: + (use default) Пожалуйста введите адрес шлюза Пожалуйста введите имя пользователя Не выбран сертификат CA Пожалуйста выберите один Выбрать автоматически + Please enter a number in the range from %1$d - %2$d EAP-TNC may affect your privacy Device data is sent to the gateway operator <p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.</p><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.</p><b>Any data will be sent only after verifying the gateway\'s identity.</b> diff --git a/src/frontends/android/res/values-ua/strings.xml b/src/frontends/android/res/values-ua/strings.xml index e23b9b9b2..19587b219 100644 --- a/src/frontends/android/res/values-ua/strings.xml +++ b/src/frontends/android/res/values-ua/strings.xml @@ -61,11 +61,15 @@ Вибрати автоматично Вибрати сертифікат CA Вибрати спеціальний сертифікат CA + Show advanced settings + MTU: + (use default) Введіть адресу шлюза тут Введіть ім\'я користувача тут Не вибрано сертифікат CA Будь ласка виберіть один Вибрати автоматично + Please enter a number in the range from %1$d - %2$d EAP-TNC may affect your privacy Device data is sent to the gateway operator <p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.</p><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.</p><b>Any data will be sent only after verifying the gateway\'s identity.</b> diff --git a/src/frontends/android/res/values/strings.xml b/src/frontends/android/res/values/strings.xml index 933a80aff..3e9d7337c 100644 --- a/src/frontends/android/res/values/strings.xml +++ b/src/frontends/android/res/values/strings.xml @@ -1,6 +1,6 @@ Please enter the gateway address here Please enter your username here No CA certificate selected Please select one or activate Select automatically + Please enter a number in the range from %1$d - %2$d EAP-TNC may affect your privacy Device data is sent to the gateway operator <p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.</p><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.</p><b>Any data will be sent only after verifying the gateway\'s identity.</b> 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..8d8e07f9d 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,9 @@ public class VpnProfileDetailActivity extends Activity private CheckBox mCheckAuto; private RelativeLayout mSelectCert; private RelativeLayout mTncNotice; + private CheckBox mShowAdvanced; + private ViewGroup mAdvancedSettings; + private EditText mMTU; @Override public void onCreate(Bundle savedInstanceState) @@ -108,6 +113,11 @@ 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); + mSelectVpnType.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) @@ -154,6 +164,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 +183,7 @@ public class VpnProfileDetailActivity extends Activity updateCredentialView(); updateCertificateSelector(); + updateAdvancedSettings(); } @Override @@ -314,6 +333,21 @@ 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) + { + show = mProfile.getMTU() != null; + } + 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 +402,12 @@ 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; + } return valid; } @@ -395,6 +435,7 @@ public class VpnProfileDetailActivity extends Activity } String certAlias = mCheckAuto.isChecked() ? null : mCertEntry.getAlias(); mProfile.setCertificateAlias(certAlias); + mProfile.setMTU(getInteger(mMTU)); } /** @@ -417,6 +458,7 @@ 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); useralias = mProfile.getUserCertificateAlias(); alias = mProfile.getCertificateAlias(); getActionBar().setTitle(mProfile.getName()); @@ -458,6 +500,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 From 4d02c49eadc00625189c5466286b9f670e47e51f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 16 Jun 2015 16:06:13 +0200 Subject: [PATCH 18/32] android: Apply configured MTU While it is stored as property of individual profiles it is really a global setting because we currently don't support more than one connection. --- .../jni/libandroidbridge/backend/android_service.c | 12 ++++++++---- .../android/jni/libandroidbridge/charonservice.c | 11 ++++++++--- .../android/jni/libandroidbridge/charonservice.h | 5 +++++ .../strongswan/android/logic/CharonVpnService.java | 1 + 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index b11e66464..34fe4dac9 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -32,8 +32,6 @@ typedef struct private_android_service_t private_android_service_t; -#define TUN_DEFAULT_MTU 1400 - /** * private data of Android service */ @@ -69,6 +67,11 @@ struct private_android_service_t { */ int tunfd; + /** + * MTU of TUN device + */ + int mtu; + /** * DNS proxy */ @@ -176,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) { @@ -294,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; } @@ -827,6 +830,7 @@ android_service_t *android_service_create(android_creds_t *creds, .settings = settings, .creds = creds, .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, diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.c b/src/frontends/android/jni/libandroidbridge/charonservice.c index 5b4d649ae..f94da0515 100644 --- a/src/frontends/android/jni/libandroidbridge/charonservice.c +++ b/src/frontends/android/jni/libandroidbridge/charonservice.c @@ -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; @@ -409,6 +408,14 @@ static void initiate(settings_t *settings) 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); @@ -467,8 +474,6 @@ 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, 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 @@ -44,6 +44,11 @@ typedef enum android_vpn_state_t android_vpn_state_t; 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 */ 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 a79d9f8c1..ffe14491c 100644 --- a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java +++ b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java @@ -219,6 +219,7 @@ public class CharonVpnService extends VpnService implements Runnable Log.i(TAG, "charon started"); 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.username", mCurrentProfile.getUsername()); From 5b11855f26d083726e6c6ac4fa1b939d11b548e9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 16 Jun 2015 16:39:08 +0200 Subject: [PATCH 19/32] android: Add field for server port to data model --- .../org/strongswan/android/data/VpnProfile.java | 12 +++++++++++- .../android/data/VpnProfileDataSource.java | 14 ++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) 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 306f18b25..6b8378337 100644 --- a/src/frontends/android/src/org/strongswan/android/data/VpnProfile.java +++ b/src/frontends/android/src/org/strongswan/android/data/VpnProfile.java @@ -20,7 +20,7 @@ package org.strongswan.android.data; public class VpnProfile implements Cloneable { private String mName, mGateway, mUsername, mPassword, mCertificate, mUserCertificate; - private Integer mMTU; + private Integer mMTU, mPort; private VpnType mVpnType; private long mId = -1; @@ -114,6 +114,16 @@ public class VpnProfile implements Cloneable this.mMTU = mtu; } + public Integer getPort() + { + return mPort; + } + + public void setPort(Integer port) + { + this.mPort = port; + } + @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 e5e582523..b9842a99c 100644 --- a/src/frontends/android/src/org/strongswan/android/data/VpnProfileDataSource.java +++ b/src/frontends/android/src/org/strongswan/android/data/VpnProfileDataSource.java @@ -41,6 +41,7 @@ public class VpnProfileDataSource 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"; private DatabaseHelper mDbHelper; private SQLiteDatabase mDatabase; @@ -49,7 +50,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 = 5; + private static final int DATABASE_VERSION = 6; public static final String DATABASE_CREATE = "CREATE TABLE " + TABLE_VPNPROFILE + " (" + @@ -61,7 +62,8 @@ public class VpnProfileDataSource KEY_PASSWORD + " TEXT," + KEY_CERTIFICATE + " TEXT," + KEY_USER_CERTIFICATE + " TEXT," + - KEY_MTU + " INTEGER" + + KEY_MTU + " INTEGER," + + KEY_PORT + " INTEGER" + ");"; private static final String[] ALL_COLUMNS = new String[] { KEY_ID, @@ -73,6 +75,7 @@ public class VpnProfileDataSource KEY_CERTIFICATE, KEY_USER_CERTIFICATE, KEY_MTU, + KEY_PORT, }; private static class DatabaseHelper extends SQLiteOpenHelper @@ -112,6 +115,11 @@ public class VpnProfileDataSource db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_MTU + " INTEGER;"); } + if (oldVersion < 6) + { + db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_PORT + + " INTEGER;"); + } } private void updateColumns(SQLiteDatabase db) @@ -264,6 +272,7 @@ public class VpnProfileDataSource 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))); return profile; } @@ -278,6 +287,7 @@ public class VpnProfileDataSource 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()); return values; } From 6c0ec35c538561c0bdbcf765d94e33d5390dbbe3 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 16 Jun 2015 16:42:40 +0200 Subject: [PATCH 20/32] android: Extend GUI so the server port can be entered --- .../android/res/layout/profile_detail_view.xml | 14 ++++++++++++++ src/frontends/android/res/values-de/strings.xml | 1 + src/frontends/android/res/values-pl/strings.xml | 1 + src/frontends/android/res/values-ru/strings.xml | 1 + src/frontends/android/res/values-ua/strings.xml | 1 + src/frontends/android/res/values/strings.xml | 1 + .../android/ui/VpnProfileDetailActivity.java | 12 +++++++++++- 7 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/frontends/android/res/layout/profile_detail_view.xml b/src/frontends/android/res/layout/profile_detail_view.xml index 15fe42401..089f0655b 100644 --- a/src/frontends/android/res/layout/profile_detail_view.xml +++ b/src/frontends/android/res/layout/profile_detail_view.xml @@ -165,6 +165,20 @@ android:inputType="number|textNoSuggestions" android:hint="@string/profile_use_default_hint" /> + + + + diff --git a/src/frontends/android/res/values-de/strings.xml b/src/frontends/android/res/values-de/strings.xml index 4dfffb709..6cdb5b37d 100644 --- a/src/frontends/android/res/values-de/strings.xml +++ b/src/frontends/android/res/values-de/strings.xml @@ -65,6 +65,7 @@ Wählen Sie ein bestimmtes CA-Zertifikat Erweiterte Einstellungen anzeigen MTU: + Server Port: (Standardwert verwenden) Bitte geben Sie hier die Gateway-Adresse ein diff --git a/src/frontends/android/res/values-pl/strings.xml b/src/frontends/android/res/values-pl/strings.xml index a46e8da2a..87c6a2726 100644 --- a/src/frontends/android/res/values-pl/strings.xml +++ b/src/frontends/android/res/values-pl/strings.xml @@ -65,6 +65,7 @@ Wybierz określony certyfikat CA Show advanced settings MTU: + Server port: (use default) Wprowadź adres bramki diff --git a/src/frontends/android/res/values-ru/strings.xml b/src/frontends/android/res/values-ru/strings.xml index 7fbe231d3..4164f61ca 100644 --- a/src/frontends/android/res/values-ru/strings.xml +++ b/src/frontends/android/res/values-ru/strings.xml @@ -62,6 +62,7 @@ Выбрать CA сертификат Show advanced settings MTU: + Server port: (use default) Пожалуйста введите адрес шлюза diff --git a/src/frontends/android/res/values-ua/strings.xml b/src/frontends/android/res/values-ua/strings.xml index 19587b219..7e3a73531 100644 --- a/src/frontends/android/res/values-ua/strings.xml +++ b/src/frontends/android/res/values-ua/strings.xml @@ -63,6 +63,7 @@ Вибрати спеціальний сертифікат CA Show advanced settings MTU: + Server port: (use default) Введіть адресу шлюза тут diff --git a/src/frontends/android/res/values/strings.xml b/src/frontends/android/res/values/strings.xml index 3e9d7337c..3c067611d 100644 --- a/src/frontends/android/res/values/strings.xml +++ b/src/frontends/android/res/values/strings.xml @@ -65,6 +65,7 @@ Select a specific CA certificate Show advanced settings MTU: + Server port: (use default) Please enter the gateway address here 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 8d8e07f9d..ff1625c62 100644 --- a/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java +++ b/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java @@ -84,6 +84,7 @@ public class VpnProfileDetailActivity extends Activity private CheckBox mShowAdvanced; private ViewGroup mAdvancedSettings; private EditText mMTU; + private EditText mPort; @Override public void onCreate(Bundle savedInstanceState) @@ -117,6 +118,7 @@ public class VpnProfileDetailActivity extends Activity mAdvancedSettings = (ViewGroup)findViewById(R.id.advanced_settings); mMTU = (EditText)findViewById(R.id.mtu); + mPort = (EditText)findViewById(R.id.port); mSelectVpnType.setOnItemSelectedListener(new OnItemSelectedListener() { @Override @@ -342,7 +344,7 @@ public class VpnProfileDetailActivity extends Activity boolean show = mShowAdvanced.isChecked(); if (!show && mProfile != null) { - show = mProfile.getMTU() != null; + show = mProfile.getMTU() != null || mProfile.getPort() != null; } mShowAdvanced.setVisibility(!show ? View.VISIBLE : View.GONE); mAdvancedSettings.setVisibility(show ? View.VISIBLE : View.GONE); @@ -408,6 +410,12 @@ public class VpnProfileDetailActivity extends Activity 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; } @@ -436,6 +444,7 @@ public class VpnProfileDetailActivity extends Activity String certAlias = mCheckAuto.isChecked() ? null : mCertEntry.getAlias(); mProfile.setCertificateAlias(certAlias); mProfile.setMTU(getInteger(mMTU)); + mProfile.setPort(getInteger(mPort)); } /** @@ -459,6 +468,7 @@ public class VpnProfileDetailActivity extends Activity 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); useralias = mProfile.getUserCertificateAlias(); alias = mProfile.getCertificateAlias(); getActionBar().setTitle(mProfile.getName()); From cda167c8c46d10c5d5f22bc6ace6c9183ed161fd Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 16 Jun 2015 16:44:29 +0200 Subject: [PATCH 21/32] android: Apply configured server port --- .../android/jni/libandroidbridge/backend/android_service.c | 5 ++++- .../src/org/strongswan/android/logic/CharonVpnService.java | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index 34fe4dac9..7ef3913f7 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -681,11 +681,14 @@ static job_requeue_t initiate(private_android_service_t *this) } }; 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), - server, 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)); 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 ffe14491c..236e759f8 100644 --- a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java +++ b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java @@ -222,6 +222,7 @@ public class CharonVpnService extends VpnService implements Runnable 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()); From 69e0215b9db103f2fe58b7576a7fb5c17a1c7cdd Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 17 Jun 2015 15:31:24 +0200 Subject: [PATCH 22/32] android: Manually load libraries with dlopen() and RTLD_GLOBAL on Android M This fixes an issue when using the Android M preview. Bionic's dynamic linker was changed so that symbols in libraries loaded with RTLD_LOCAL were not found anymore in dlsym(RTLD_DEFAULT, ...). This is the case for libraries loaded with System.loadLibrary(), therefore, the plugin loader in libstrongswan was not able to resolve any symbols defined in other libraries loaded later. While this seems to have been broken unintentionally for existing apps (fix at [1]), it will again be a problem whenever we decide to increase targetSdkVersion beyond 22 (or until that fix makes it into the system/emulator images). Unfortunately, the dynamic loader in releases prior to Android 4.3 can't load libandroidbridge without also loading its dependencies. [1] https://github.com/android/platform_bionic/commit/1913352c6b --- .../jni/libandroidbridge/android_jni.c | 39 ++++++++++++++++++- .../android/logic/CharonVpnService.java | 26 +++++++------ 2 files changed, 53 insertions(+), 12 deletions(-) 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 + #include "android_jni.h" #include @@ -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/src/org/strongswan/android/logic/CharonVpnService.java b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java index 236e759f8..482caa220 100644 --- a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java +++ b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java @@ -42,6 +42,7 @@ 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; @@ -732,22 +733,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"); } } From 1082372448d8055b9ab2cd296a011b2f71eee3fe Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 17 Jun 2015 17:21:21 +0200 Subject: [PATCH 23/32] android: Add a custom kernel-net implementation to replace kernel-netlink When roaming from a mobile network to WiFi on Android 5.x the event received via ConnectivityManager is triggered before the mobile connection is fully torn down (i.e. before the interface is disabled and the routes disappear). So for strongSwan the current path still seems valid and since no roam event is triggered later the daemon never switches to WiFi and the connection is broken afterwards. A possible solution to this is enabling roam events in the kernel-netlink plugin. That would trigger an event when the device is finally disconnected from the mobile network. However, this could actually take a some time, during which traffic continues to be sent via mobile network instead of WiFi. That's because Android now uses multiple routing tables, routing rules and fwmarks to direct traffic to the appropriate interface/table, but in our plugin we don't have the information available that would allow us to make the switch to a different network/routing table earlier (and we actually prefer the current path if it is still valid). Additionally, the plugin produces quite a bit more events than ConnectivityManager (which was one of the reasons to use the latter in the first place). This custom kernel-net implementation is now specifically tailored for Android. Roam events are still triggered via ConnectivityManager but the source address is determined via connect()/getsockname() on a VPN excluded UDP socket, which does use the correct routing table as intended by Android. That way the daemon immediately sees a different source IP when connectivity changes even if the device is connected to multiple networks concurrently. --- src/frontends/android/jni/Android.mk | 2 +- .../jni/libandroidbridge/charonservice.c | 22 +-- .../jni/libandroidbridge/kernel/android_net.c | 153 ++++++++++++++++-- .../jni/libandroidbridge/kernel/android_net.h | 23 +-- 4 files changed, 152 insertions(+), 48 deletions(-) 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/libandroidbridge/charonservice.c b/src/frontends/android/jni/libandroidbridge/charonservice.c index f94da0515..2655f7361 100644 --- a/src/frontends/android/jni/libandroidbridge/charonservice.c +++ b/src/frontends/android/jni/libandroidbridge/charonservice.c @@ -82,11 +82,6 @@ struct private_charonservice_t { */ network_manager_t *network_manager; - /** - * Handle network events - */ - android_net_t *net_handler; - /** * CharonVpnService reference */ @@ -431,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); @@ -491,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, @@ -527,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"), diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c index 653e2738b..9cab74e13 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,6 +11,10 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ +#include +#include +#include +#include #include "android_net.h" @@ -29,7 +33,7 @@ struct private_android_net_t { /** * Public kernel interface */ - android_net_t public; + kernel_net_t public; /** * Reference to NetworkManager object @@ -37,14 +41,24 @@ 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; }; /** @@ -83,32 +97,151 @@ 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; + + 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 (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)); + } + 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_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->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->network_manager->add_connectivity_cb(this->network_manager, (void*)connectivity_cb, this); 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 - -typedef struct android_net_t android_net_t; +#include /** - * Handle connectivity events from NetworkManager - */ -struct android_net_t { - - /** - * Destroy an android_net_t instance. - */ - void (*destroy)(android_net_t *this); -}; - -/** - * 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_ @}*/ From 5da31733de79baf9249b45be1132c1e2f7203f4e Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 22 Jun 2015 17:15:33 +0200 Subject: [PATCH 24/32] android: Fix kernel-net implementation on Android 4.3 and earlier Before fwmarks were used protected sockets were bound to the outbound interface via SO_BINDTODEVICE. This does not always seem to work well together with our connect()/getsockname() trick if the server is covered by the traffic selectors. Calling protect() again after disconnecting the socket seems to help, but if there is no connectivity at all we still get the virtual IP back (maybe protect() does not bind the socket to any interface then). --- .../jni/libandroidbridge/android_jni.h | 2 ++ .../jni/libandroidbridge/kernel/android_net.c | 34 +++++++++++++++++++ 2 files changed, 36 insertions(+) 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/kernel/android_net.c b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c index 9cab74e13..73322ad76 100644 --- a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c +++ b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c @@ -18,6 +18,7 @@ #include "android_net.h" +#include "../android_jni.h" #include "../charonservice.h" #include #include @@ -114,6 +115,11 @@ METHOD(kernel_net_t, get_source_addr, host_t*, DBG1(DBG_KNL, "failed to disconnect socket: %s", strerror(errno)); return NULL; } + if (android_sdk_version <= ANDROID_JELLY_BEAN_MR2) + { /* this seems to help avoiding the VIP, unless there is no connectivity + * at all */ + charonservice->bypass_socket(charonservice, -1, 0); + } if (connect(this->socket_v4, dest->get_sockaddr(dest), addrlen) < 0) { /* don't report an error if we are not connected (ENETUNREACH) */ @@ -131,6 +137,29 @@ METHOD(kernel_net_t, get_source_addr, host_t*, return host_create_from_sockaddr((sockaddr_t*)&addr); } +METHOD(kernel_net_t, get_source_addr_old, host_t*, + private_android_net_t *this, host_t *dest, host_t *src) +{ + host_t *host; + + /* on older Android versions we might get the virtual IP back because + * the protect() implementation there and connect() don't properly work + * together, on newer releases (using fwmarks) that's not a problem */ + host = get_source_addr(this, dest, src); + if (host) + { + this->mutex->lock(this->mutex); + if (this->vips->find_first(this->vips, (void*)host->ip_equals, + NULL, host) == SUCCESS) + { + host->destroy(host); + host = NULL; + } + this->mutex->unlock(this->mutex); + } + return host; +} + METHOD(kernel_net_t, get_nexthop, host_t*, private_android_net_t *this, host_t *dest, int prefix, host_t *src) { @@ -233,6 +262,11 @@ kernel_net_t *kernel_android_net_create() ); timerclear(&this->next_roam); + if (android_sdk_version <= ANDROID_JELLY_BEAN_MR2) + { + this->public.get_source_addr = _get_source_addr_old; + } + this->socket_v4 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (this->socket_v4 < 0) { From 41b59a3443a862825d9b59d613b9fa27bc4174da Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 21 Jul 2015 11:05:10 +0200 Subject: [PATCH 25/32] android: Add method to check for connectivity to NetworkManager --- .../libandroidbridge/kernel/network_manager.c | 27 +++++++++++++++++-- .../libandroidbridge/kernel/network_manager.h | 9 ++++++- .../android/logic/NetworkManager.java | 17 +++++++++--- 3 files changed, 46 insertions(+), 7 deletions(-) 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 @@ -65,6 +65,13 @@ struct network_manager_t { void (*remove_connectivity_cb)(network_manager_t *this, 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 */ 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()); } /** From 030bcee1b0f8cebce7cce1a87e33b98f3e1ed784 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 21 Jul 2015 11:14:53 +0200 Subject: [PATCH 26/32] android: Retrigger a roam event if reportedly connected but no source address found In dual-stack environments the IPv6 connectivity (via autoconfiguration) might be established before the IPv4 connectivity (via DHCP). It seems Android triggers the CONNECTIVITY_ACTION broadcast already when the first family is fully configured. At that time we might not be able to find an IPv4 source address. And since Android does not trigger the broadcast again if IPv4 connectivity is established, the connection is broken afterwards. So we store the connectivity state and if we are reportedly connected but still find no source address we trigger a roam event to recheck for an IPv4 address. This will cause regular rechecks if a device enters an IPv6-only network, but I guess that's rare (otherwise we could limit the number of rechecks done between connectivity changes). --- .../jni/libandroidbridge/kernel/android_net.c | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c index 73322ad76..2ce1bdfac 100644 --- a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c +++ b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c @@ -26,6 +26,7 @@ /** delay before firing roam events (ms) */ #define ROAM_DELAY 100 +#define ROAM_DELAY_RECHECK 1000 typedef struct private_android_net_t private_android_net_t; @@ -60,6 +61,11 @@ struct private_android_net_t { * Socket used to determine source address */ int socket_v4; + + /** + * Whether the device is currently connected + */ + bool connected; }; /** @@ -84,6 +90,7 @@ static void connectivity_cb(private_android_net_t *this, time_monotonic(&now); this->mutex->lock(this->mutex); + this->connected = !disconnected; if (!timercmp(&now, &this->next_roam, >)) { this->mutex->unlock(this->mutex); @@ -107,6 +114,8 @@ METHOD(kernel_net_t, get_source_addr, host_t*, struct sockaddr_in6 sin6; } addr; socklen_t addrlen; + timeval_t now; + job_t *job; addrlen = *dest->get_sockaddr_len(dest); addr.sockaddr.sa_family = AF_UNSPEC; @@ -127,6 +136,27 @@ METHOD(kernel_net_t, get_source_addr, host_t*, { DBG1(DBG_KNL, "failed to connect socket: %s", strerror(errno)); } + else + { + time_monotonic(&now); + this->mutex->lock(this->mutex); + if (this->connected && timercmp(&now, &this->next_roam, >)) + { /* we were not able to find a source address but reportedly are + * connected, trigger a recheck in case an IP address appears + * delayed but the callback is not triggered again */ + timeval_add_ms(&now, ROAM_DELAY_RECHECK); + this->next_roam = now; + this->mutex->unlock(this->mutex); + job = (job_t*)callback_job_create((callback_job_cb_t)roam_event, + NULL, NULL, NULL); + lib->scheduler->schedule_job_ms(lib->scheduler, job, + ROAM_DELAY_RECHECK); + } + else + { + this->mutex->unlock(this->mutex); + } + } return NULL; } if (getsockname(this->socket_v4, &addr.sockaddr, &addrlen) < 0) @@ -275,7 +305,10 @@ kernel_net_t *kernel_android_net_create() } charonservice->bypass_socket(charonservice, this->socket_v4, AF_INET); - this->network_manager->add_connectivity_cb(this->network_manager, - (void*)connectivity_cb, this); + this->mutex->lock(this->mutex); + this->network_manager->add_connectivity_cb( + this->network_manager, (void*)connectivity_cb, this); + this->connected = this->network_manager->is_connected(this->network_manager); + this->mutex->unlock(this->mutex); return &this->public; } From f3d8da7644012f31b1bfbfdbec8ad7b21017e844 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 24 Jul 2015 11:33:07 +0200 Subject: [PATCH 27/32] android: Add field for split tunneling config to data model --- .../org/strongswan/android/data/VpnProfile.java | 17 ++++++++++++++++- .../android/data/VpnProfileDataSource.java | 14 ++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) 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 6b8378337..5c64ad0e5 100644 --- a/src/frontends/android/src/org/strongswan/android/data/VpnProfile.java +++ b/src/frontends/android/src/org/strongswan/android/data/VpnProfile.java @@ -17,10 +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; + private Integer mMTU, mPort, mSplitTunneling; private VpnType mVpnType; private long mId = -1; @@ -124,6 +129,16 @@ public class VpnProfile implements Cloneable 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 b9842a99c..45e9b8650 100644 --- a/src/frontends/android/src/org/strongswan/android/data/VpnProfileDataSource.java +++ b/src/frontends/android/src/org/strongswan/android/data/VpnProfileDataSource.java @@ -42,6 +42,7 @@ public class VpnProfileDataSource 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; @@ -50,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 = 6; + private static final int DATABASE_VERSION = 7; public static final String DATABASE_CREATE = "CREATE TABLE " + TABLE_VPNPROFILE + " (" + @@ -63,7 +64,8 @@ public class VpnProfileDataSource KEY_CERTIFICATE + " TEXT," + KEY_USER_CERTIFICATE + " TEXT," + KEY_MTU + " INTEGER," + - KEY_PORT + " INTEGER" + + KEY_PORT + " INTEGER," + + KEY_SPLIT_TUNNELING + " INTEGER" + ");"; private static final String[] ALL_COLUMNS = new String[] { KEY_ID, @@ -76,6 +78,7 @@ public class VpnProfileDataSource KEY_USER_CERTIFICATE, KEY_MTU, KEY_PORT, + KEY_SPLIT_TUNNELING, }; private static class DatabaseHelper extends SQLiteOpenHelper @@ -120,6 +123,11 @@ public class VpnProfileDataSource 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) @@ -273,6 +281,7 @@ public class VpnProfileDataSource 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; } @@ -288,6 +297,7 @@ public class VpnProfileDataSource 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; } From 3ee84fa9762245edc403f56a24f465428c935e41 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 24 Jul 2015 11:47:53 +0200 Subject: [PATCH 28/32] android: Extend GUI so the split tunneling options can be set --- .../android/res/layout/profile_detail_view.xml | 18 ++++++++++++++++++ .../android/res/values-de/strings.xml | 3 +++ .../android/res/values-pl/strings.xml | 3 +++ .../android/res/values-ru/strings.xml | 3 +++ .../android/res/values-ua/strings.xml | 3 +++ src/frontends/android/res/values/strings.xml | 3 +++ .../android/ui/VpnProfileDetailActivity.java | 13 ++++++++++++- 7 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/frontends/android/res/layout/profile_detail_view.xml b/src/frontends/android/res/layout/profile_detail_view.xml index 089f0655b..57d5606ff 100644 --- a/src/frontends/android/res/layout/profile_detail_view.xml +++ b/src/frontends/android/res/layout/profile_detail_view.xml @@ -179,6 +179,24 @@ android:inputType="number|textNoSuggestions" android:hint="@string/profile_use_default_hint" /> + + + + + + diff --git a/src/frontends/android/res/values-de/strings.xml b/src/frontends/android/res/values-de/strings.xml index 6cdb5b37d..6cd5ba50a 100644 --- a/src/frontends/android/res/values-de/strings.xml +++ b/src/frontends/android/res/values-de/strings.xml @@ -67,6 +67,9 @@ MTU: Server Port: (Standardwert verwenden) + Split-Tunneling: + Blockiere IPv4 Verkehr der nicht für das VPN bestimmt ist + Blockiere IPv6 Verkehr der nicht für das VPN bestimmt ist Bitte geben Sie hier die Gateway-Adresse ein Bitte geben Sie hier Ihren Benutzernamen ein diff --git a/src/frontends/android/res/values-pl/strings.xml b/src/frontends/android/res/values-pl/strings.xml index 87c6a2726..fb2aba003 100644 --- a/src/frontends/android/res/values-pl/strings.xml +++ b/src/frontends/android/res/values-pl/strings.xml @@ -67,6 +67,9 @@ MTU: Server port: (use default) + Split tunneling: + Block IPv4 traffic not destined for the VPN + Block IPv6 traffic not destined for the VPN Wprowadź adres bramki Wprowadź swoją nazwę użytkownika diff --git a/src/frontends/android/res/values-ru/strings.xml b/src/frontends/android/res/values-ru/strings.xml index 4164f61ca..eabfc084b 100644 --- a/src/frontends/android/res/values-ru/strings.xml +++ b/src/frontends/android/res/values-ru/strings.xml @@ -64,6 +64,9 @@ MTU: Server port: (use default) + Split tunneling: + Block IPv4 traffic not destined for the VPN + Block IPv6 traffic not destined for the VPN Пожалуйста введите адрес шлюза Пожалуйста введите имя пользователя diff --git a/src/frontends/android/res/values-ua/strings.xml b/src/frontends/android/res/values-ua/strings.xml index 7e3a73531..d7c238370 100644 --- a/src/frontends/android/res/values-ua/strings.xml +++ b/src/frontends/android/res/values-ua/strings.xml @@ -65,6 +65,9 @@ MTU: Server port: (use default) + Split tunneling: + Block IPv4 traffic not destined for the VPN + Block IPv6 traffic not destined for the VPN Введіть адресу шлюза тут Введіть ім\'я користувача тут diff --git a/src/frontends/android/res/values/strings.xml b/src/frontends/android/res/values/strings.xml index 3c067611d..5c8ebab57 100644 --- a/src/frontends/android/res/values/strings.xml +++ b/src/frontends/android/res/values/strings.xml @@ -67,6 +67,9 @@ MTU: Server port: (use default) + Split tunneling: + Block IPv4 traffic not destined for the VPN + Block IPv6 traffic not destined for the VPN Please enter the gateway address here Please enter your username here 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 ff1625c62..a8b3daa06 100644 --- a/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java +++ b/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java @@ -85,6 +85,8 @@ public class VpnProfileDetailActivity extends Activity private ViewGroup mAdvancedSettings; private EditText mMTU; private EditText mPort; + private CheckBox mBlockIPv4; + private CheckBox mBlockIPv6; @Override public void onCreate(Bundle savedInstanceState) @@ -119,6 +121,8 @@ public class VpnProfileDetailActivity extends Activity 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 @@ -344,7 +348,8 @@ public class VpnProfileDetailActivity extends Activity boolean show = mShowAdvanced.isChecked(); if (!show && mProfile != null) { - show = mProfile.getMTU() != null || mProfile.getPort() != 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); @@ -445,6 +450,10 @@ public class VpnProfileDetailActivity extends Activity 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); } /** @@ -469,6 +478,8 @@ public class VpnProfileDetailActivity extends Activity 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()); From bb40498df338994b052701c3db420961062d1ecf Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 21 Jul 2015 11:37:33 +0200 Subject: [PATCH 29/32] android: Build against the android-21 target --- src/frontends/android/project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 3390092c76517cf40d73c98ea431712c9147b01f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 21 Jul 2015 11:57:56 +0200 Subject: [PATCH 30/32] include: Remove linux/types.h header This conflicts with the headers of the android-21 target. Hopefully headers on current Linux distros are recent enough. --- src/include/Makefile.am | 3 +- src/include/linux/types.h | 172 -------------------------------------- 2 files changed, 1 insertion(+), 174 deletions(-) delete mode 100644 src/include/linux/types.h diff --git a/src/include/Makefile.am b/src/include/Makefile.am index 5de713143..0284c094a 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -1,3 +1,2 @@ EXTRA_DIST = linux/if_alg.h linux/ipsec.h linux/netlink.h linux/rtnetlink.h \ - linux/pfkeyv2.h linux/udp.h linux/xfrm.h linux/types.h \ - sys/queue.h + linux/pfkeyv2.h linux/udp.h linux/xfrm.h sys/queue.h diff --git a/src/include/linux/types.h b/src/include/linux/types.h deleted file mode 100644 index 22cfdc05e..000000000 --- a/src/include/linux/types.h +++ /dev/null @@ -1,172 +0,0 @@ -#ifndef _LINUX_TYPES_H -#define _LINUX_TYPES_H - - -#include -#include - -#ifndef __KERNEL_STRICT_NAMES - -typedef __u32 __kernel_dev_t; - -typedef __kernel_fd_set fd_set; -typedef __kernel_dev_t dev_t; -typedef __kernel_ino_t ino_t; -typedef __kernel_mode_t mode_t; -typedef __kernel_nlink_t nlink_t; -typedef __kernel_off_t off_t; -typedef __kernel_pid_t pid_t; -typedef __kernel_daddr_t daddr_t; -typedef __kernel_key_t key_t; -typedef __kernel_suseconds_t suseconds_t; -typedef __kernel_timer_t timer_t; -typedef __kernel_clockid_t clockid_t; -typedef __kernel_mqd_t mqd_t; - -typedef __kernel_uid_t uid_t; -typedef __kernel_gid_t gid_t; - -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) -typedef __kernel_loff_t loff_t; -#endif - -/* - * The following typedefs are also protected by individual ifdefs for - * historical reasons: - */ -#ifndef _SIZE_T -#define _SIZE_T -typedef __kernel_size_t size_t; -#endif - -#ifndef _SSIZE_T -#define _SSIZE_T -typedef __kernel_ssize_t ssize_t; -#endif - -#ifndef _PTRDIFF_T -#define _PTRDIFF_T -typedef __kernel_ptrdiff_t ptrdiff_t; -#endif - -#ifndef _TIME_T -#define _TIME_T -typedef __kernel_time_t time_t; -#endif - -#ifndef _CLOCK_T -#define _CLOCK_T -typedef __kernel_clock_t clock_t; -#endif - -#ifndef _CADDR_T -#define _CADDR_T -typedef __kernel_caddr_t caddr_t; -#endif - -/* bsd */ -typedef unsigned char u_char; -typedef unsigned short u_short; -typedef unsigned int u_int; -typedef unsigned long u_long; - -/* sysv */ -typedef unsigned char unchar; -typedef unsigned short ushort; -typedef unsigned int uint; -typedef unsigned long ulong; - -#ifndef __BIT_TYPES_DEFINED__ -#define __BIT_TYPES_DEFINED__ - -typedef __u8 u_int8_t; -typedef __s8 int8_t; -typedef __u16 u_int16_t; -typedef __s16 int16_t; -typedef __u32 u_int32_t; -typedef __s32 int32_t; - -#endif /* !(__BIT_TYPES_DEFINED__) */ - -typedef __u8 uint8_t; -typedef __u16 uint16_t; -typedef __u32 uint32_t; - -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) -typedef __u64 uint64_t; -typedef __u64 u_int64_t; -typedef __s64 int64_t; -#endif - -/* this is a special 64bit data type that is 8-byte aligned */ -#define aligned_u64 unsigned long long __attribute__((aligned(8))) -#define aligned_be64 __be64 __attribute__((aligned(8))) -#define aligned_le64 __le64 __attribute__((aligned(8))) - -/** - * The type used for indexing onto a disc or disc partition. - * - * Linux always considers sectors to be 512 bytes long independently - * of the devices real block size. - */ -#ifdef CONFIG_LBD -typedef u64 sector_t; -#else -typedef unsigned long sector_t; -#endif - -/* - * The type of the inode's block count. - */ -#ifdef CONFIG_LSF -typedef u64 blkcnt_t; -#else -typedef unsigned long blkcnt_t; -#endif - -/* - * The type of an index into the pagecache. Use a #define so asm/types.h - * can override it. - */ -#ifndef pgoff_t -#define pgoff_t unsigned long -#endif - -#endif /* __KERNEL_STRICT_NAMES */ - -/* - * Below are truly Linux-specific types that should never collide with - * any application/library that wants linux/types.h. - */ - -#ifdef __CHECKER__ -#define __bitwise__ __attribute__((bitwise)) -#else -#define __bitwise__ -#endif -#ifdef __CHECK_ENDIAN__ -#define __bitwise __bitwise__ -#else -#define __bitwise -#endif - -typedef __u16 __bitwise __le16; -typedef __u16 __bitwise __be16; -typedef __u32 __bitwise __le32; -typedef __u32 __bitwise __be32; -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) -typedef __u64 __bitwise __le64; -typedef __u64 __bitwise __be64; -#endif -typedef __u16 __bitwise __sum16; -typedef __u32 __bitwise __wsum; - - -struct ustat { - __kernel_daddr_t f_tfree; - __kernel_ino_t f_tinode; - char f_fname[6]; - char f_fpack[6]; -}; - -#endif /* _LINUX_TYPES_H */ From acc8948fa50311c76b58c49c038dc222145f65de Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 24 Jul 2015 14:14:07 +0200 Subject: [PATCH 31/32] android: Apply split tunneling options when creating TUN device Android blocks traffic for address families for which no IPs, DNS servers or routes are installed via VpnService.Builder. Since Android 5+ (API level 21) it is possible to explicitly allow such traffic to bypass the VPN. So for proper split tunneling we note whether we saw a VIP and/or DNS server of a specific family, and if not, allow traffic of that family to bypass the VPN using the new API (on older systems there is no change and such traffic will still be blocked). Otherwise, we do what we did so far, that is, simply install the received routes (traffic selectors), all other traffic will not be directed to the TUN device and use the underlying network instead. If traffic for a family should be blocked we install a default route via TUN device even if we received more specific traffic selectors from the server. libipsec will use the actual traffic selectors as IPsec policies and drop any packets it received that don't match them. We only do this if we saw any VIPs or DNS servers of a family. Otherwise the traffic for that family is blocked anyway. --- .../android/logic/CharonVpnService.java | 122 ++++++++++++++++-- 1 file changed, 111 insertions(+), 11 deletions(-) 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 482caa220..e5241d5a7 100644 --- a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java +++ b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java @@ -18,6 +18,10 @@ 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; @@ -35,6 +39,7 @@ 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; @@ -48,6 +53,7 @@ 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 @@ -214,7 +220,7 @@ 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"); @@ -535,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) @@ -565,7 +573,6 @@ public class CharonVpnService extends VpnService implements Runnable { try { - mBuilder.addAddress(address, prefixLength); mCache.addAddress(address, prefixLength); } catch (IllegalArgumentException ex) @@ -580,6 +587,7 @@ public class CharonVpnService extends VpnService implements Runnable try { mBuilder.addDnsServer(address); + mCache.recordAddressFamily(address); } catch (IllegalArgumentException ex) { @@ -592,7 +600,6 @@ public class CharonVpnService extends VpnService implements Runnable { try { - mBuilder.addRoute(address, prefixLength); mCache.addRoute(address, prefixLength); } catch (IllegalArgumentException ex) @@ -619,7 +626,6 @@ public class CharonVpnService extends VpnService implements Runnable { try { - mBuilder.setMtu(mtu); mCache.setMtu(mtu); } catch (IllegalArgumentException ex) @@ -634,6 +640,7 @@ public class CharonVpnService extends VpnService implements Runnable ParcelFileDescriptor fd; try { + mCache.applyData(mBuilder); fd = mBuilder.establish(); } catch (Exception ex) @@ -649,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(); } @@ -687,17 +694,40 @@ public class CharonVpnService extends VpnService implements Runnable public class BuilderCache { private final List mAddresses = new ArrayList(); - private final List mRoutes = new ArrayList(); + private final List mRoutesIPv4 = new ArrayList(); + private final List mRoutesIPv6 = new ArrayList(); + 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) @@ -705,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) { - builder.addRoute(route.mAddress, route.mPrefix); + 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("::", 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; From 1e323dc1b772a42470939ab53ad295b3bc786e30 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 27 Jul 2015 17:46:06 +0200 Subject: [PATCH 32/32] android: Keep NDK API level at 19 The headers/libraries changed a lot with level 21 so that our app won't run on devices with Android < 5 when built against it. We currently don't need any new native APIs so that should be fine. --- src/frontends/android/jni/Application.mk | 1 + 1 file changed, 1 insertion(+) 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