Merge branch 'android-scheduler'

Starting with Android 6, the system will aggressively suspend apps when
the device is idle (Doze mode).  With Android 10 on a Pixel 4 this seems
to happen after about 70 minutes.  Then the scheduler thread in our
default scheduler is only woken rarely, combined with our previous use
of the monotonic clock it meant that events were executed with severe
delays and noticing that there was such a delay.  This was particularly
bad in regards to NAT keepalives as it usually meant that the device was
not reachable anymore from the outside.

Some changes here try to improve that situation, e.g. the clock is switched
to CLOCK_REALTIME (Bionic doesn't support CLOCK_BOOTTIME for condvars) so we
can measure the actual difference e.g. since the last outbound message,
other changes try to ensure that connectivity is restored after being asleep
for a while (send DPD instead of keepalive after a long delay, send DPD even
if path to peer stays the same).

However, the most significant change is the replacement of the default
scheduler with one specifically designed for Android.  It schedules
long-term events via AlarmManager, which allows waking up the app even
if the system put it to sleep.  The latter requires adding the app to the
system's battery optimization whitelist, which is requested from the
user automatically if necessary.  With this, NAT keepalives and rekeyings
are now scheduled accurately, with little changes to the battery usage.
If the app is not whitelisted (there is a setting to ignore this), events
are delayed by up to 15 minutes after about 70 minutes, so behind a NAT
the device won't be reachable from the outside afterwards (connectivity
should be restored as soon as the device is woken from deep sleep by the
user).

Fixes #3364.
This commit is contained in:
Tobias Brunner 2020-06-02 14:18:42 +02:00
commit a22a1493c3
38 changed files with 872 additions and 173 deletions

View File

@ -40,6 +40,17 @@ charon.cache_crls = no
Certification Authority (CA) to **/etc/ipsec.d/crls** (stroke) or
**/etc/swanctl/x509crl** (vici), respectively.
charon.check_current_path = no
Whether to use DPD to check if the current path still works after any
changes to interfaces/addresses.
By default, after detecting any changes to interfaces and/or addresses no
action is taken if the current path to the remote peer still looks usable.
Enabling this option will use DPD to check if the path actually still works,
or, for instance, the peer removed the state after a longer phase without
connectivity. It will also trigger a MOBIKE update if NAT mappings were
removed during the downtime.
charon.cisco_unity = no
Send Cisco Unity vendor ID payload (IKEv1 only).
@ -220,6 +231,11 @@ charon.interfaces_use
charon.keep_alive = 20s
NAT keep alive interval.
charon.keep_alive_dpd_margin = 0s
Number of seconds the keep alive interval may be exceeded before a DPD is
sent instead of a NAT keep alive (0 to disable). This is only useful if a
clock is used that includes time spent suspended (e.g. CLOCK_BOOTTIME).
charon.leak_detective.detailed = yes
Includes source file names and line numbers in leak detective output.

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2012-2018 Tobias Brunner
Copyright (C) 2012-2020 Tobias Brunner
Copyright (C) 2012 Giuliano Grassi
Copyright (C) 2012 Ralf Sager
HSR Hochschule fuer Technik Rapperswil
@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application
android:name=".logic.StrongSwanApplication"

View File

@ -0,0 +1,169 @@
/*
* Copyright (C) 2020 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
package org.strongswan.android.logic;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.UUID;
import androidx.annotation.RequiresApi;
public class Scheduler extends BroadcastReceiver
{
private final String EXECUTE_JOB = "org.strongswan.android.Scheduler.EXECUTE_JOB";
private final Context mContext;
private final AlarmManager mManager;
private final PriorityQueue<ScheduledJob> mJobs;
public Scheduler(Context context)
{
mContext = context;
mManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
mJobs = new PriorityQueue<>();
IntentFilter filter = new IntentFilter();
filter.addAction(EXECUTE_JOB);
mContext.registerReceiver(this, filter);
}
/**
* Remove all pending jobs and unregister the receiver.
* Called via JNI.
*/
public void Terminate()
{
synchronized (this)
{
mJobs.clear();
}
mManager.cancel(createIntent());
mContext.unregisterReceiver(this);
}
/**
* Allocate a job ID. Called via JNI.
*
* @return random ID for a new job
*/
public String allocateId()
{
return UUID.randomUUID().toString();
}
/**
* Create a pending intent to execute a job.
*
* @return pending intent
*/
private PendingIntent createIntent()
{
/* using component/class doesn't work with dynamic broadcast receivers */
Intent intent = new Intent(EXECUTE_JOB);
intent.setPackage(mContext.getPackageName());
return PendingIntent.getBroadcast(mContext, 0, intent, 0);
}
/**
* Schedule executing a job in the future.
* Called via JNI from different threads.
*
* @param id job ID
* @param ms delta in milliseconds when the job should be executed
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public void scheduleJob(String id, long ms)
{
synchronized (this)
{
ScheduledJob job = new ScheduledJob(id, System.currentTimeMillis() + ms);
mJobs.add(job);
if (job == mJobs.peek())
{ /* update the alarm if the job has to be executed before all others */
PendingIntent pending = createIntent();
mManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, job.Time, pending);
}
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onReceive(Context context, Intent intent)
{
ArrayList<ScheduledJob> jobs = new ArrayList<>();
long now = System.currentTimeMillis();
synchronized (this)
{
ScheduledJob job = mJobs.peek();
while (job != null)
{
if (job.Time > now)
{
break;
}
jobs.add(mJobs.remove());
job = mJobs.peek();
}
if (job != null)
{
PendingIntent pending = createIntent();
mManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, job.Time, pending);
}
}
for (ScheduledJob job : jobs)
{
executeJob(job.Id);
}
}
/**
* Execute the job with the given ID.
*
* @param id job ID
*/
public native void executeJob(String id);
/**
* Keep track of scheduled jobs.
*/
private static class ScheduledJob implements Comparable<ScheduledJob>
{
String Id;
long Time;
ScheduledJob(String id, long time)
{
Id = id;
Time = time;
}
@Override
public int compareTo(ScheduledJob o)
{
return Long.compare(Time, o.Time);
}
}
}

View File

@ -66,7 +66,7 @@ public class MainActivity extends AppCompatActivity implements OnVpnProfileSelec
ActionBar bar = getSupportActionBar();
bar.setDisplayShowHomeEnabled(true);
bar.setDisplayShowTitleEnabled(false);
bar.setIcon(R.drawable.ic_launcher);
bar.setIcon(R.mipmap.ic_app);
/* load CA certificates in a background task */
new LoadCertificatesTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

View File

@ -202,7 +202,7 @@ public class TrustedCertificateImportActivity extends AppCompatActivity
certificate = (X509Certificate)getArguments().getSerializable(VpnProfileDataSource.KEY_CERTIFICATE);
return new AlertDialog.Builder(getActivity())
.setIcon(R.drawable.ic_launcher)
.setIcon(R.mipmap.ic_app)
.setTitle(R.string.import_certificate)
.setMessage(certificate.getSubjectDN().toString())
.setPositiveButton(R.string.import_certificate, new DialogInterface.OnClickListener()

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2018 Tobias Brunner
* Copyright (C) 2012-2020 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@ -19,12 +19,18 @@ import android.app.Dialog;
import android.app.Service;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.net.Uri;
import android.net.VpnService;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
@ -36,13 +42,16 @@ import org.strongswan.android.data.VpnProfileDataSource;
import org.strongswan.android.data.VpnType.VpnTypeFeature;
import org.strongswan.android.logic.VpnStateService;
import org.strongswan.android.logic.VpnStateService.State;
import org.strongswan.android.utils.Constants;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.PreferenceManager;
public class VpnProfileControlActivity extends AppCompatActivity
{
@ -51,6 +60,7 @@ public class VpnProfileControlActivity extends AppCompatActivity
public static final String EXTRA_VPN_PROFILE_ID = "org.strongswan.android.VPN_PROFILE_ID";
private static final int PREPARE_VPN_SERVICE = 0;
private static final int ADD_TO_POWER_WHITELIST = 1;
private static final String WAITING_FOR_RESULT = "WAITING_FOR_RESULT";
private static final String PROFILE_NAME = "PROFILE_NAME";
private static final String PROFILE_REQUIRES_PASSWORD = "REQUIRES_PASSWORD";
@ -181,6 +191,29 @@ public class VpnProfileControlActivity extends AppCompatActivity
}
}
/**
* Check if we are on the system's power whitelist, if necessary, or ask the user
* to add us.
* @return true if profile can be initiated immediately
*/
private boolean checkPowerWhitelist()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
PowerManager pm = (PowerManager)this.getSystemService(Context.POWER_SERVICE);
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
if (!pm.isIgnoringBatteryOptimizations(this.getPackageName()) &&
!pref.getBoolean(Constants.PREF_IGNORE_POWER_WHITELIST, false))
{
PowerWhitelistRequired whitelist = new PowerWhitelistRequired();
mWaitingForResult = true;
whitelist.show(getSupportFragmentManager(), DIALOG_TAG);
return false;
}
}
return true;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
@ -190,11 +223,14 @@ public class VpnProfileControlActivity extends AppCompatActivity
mWaitingForResult = false;
if (resultCode == RESULT_OK && mProfileInfo != null)
{
if (mService != null)
if (checkPowerWhitelist())
{
mService.connect(mProfileInfo, true);
if (mService != null)
{
mService.connect(mProfileInfo, true);
}
finish();
}
finish();
}
else
{ /* this happens if the always-on VPN feature is activated by a different app or the user declined */
@ -207,6 +243,14 @@ public class VpnProfileControlActivity extends AppCompatActivity
VpnNotSupportedError.showWithMessage(this, R.string.vpn_not_supported_no_permission);
}
break;
case ADD_TO_POWER_WHITELIST:
mWaitingForResult = false;
if (mProfileInfo != null && mService != null)
{
mService.connect(mProfileInfo, true);
}
finish();
break;
default:
super.onActivityResult(requestCode, resultCode, data);
}
@ -531,6 +575,32 @@ public class VpnProfileControlActivity extends AppCompatActivity
}
}
/**
* Class that displays a warning before asking the user to add the app to the
* device's power whitelist.
*/
public static class PowerWhitelistRequired extends AppCompatDialogFragment
{
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.power_whitelist_title)
.setMessage(R.string.power_whitelist_text)
.setPositiveButton(android.R.string.ok, (dialog, id) -> {
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
Uri.parse("package:" + getActivity().getPackageName()));
getActivity().startActivityForResult(intent, ADD_TO_POWER_WHITELIST);
}).create();
}
@Override
public void onCancel(@NonNull DialogInterface dialog)
{
getActivity().finish();
}
}
/**
* Class representing an error message which is displayed if VpnService is
* not supported on the current device.
@ -556,7 +626,6 @@ public class VpnProfileControlActivity extends AppCompatActivity
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.vpn_not_supported_title)
.setMessage(messageId)
.setCancelable(false)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
{
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Tobias Brunner
* Copyright (C) 2018-2019 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@ -39,6 +39,7 @@ import org.strongswan.android.utils.Constants;
@TargetApi(Build.VERSION_CODES.N)
public class VpnTileService extends TileService implements VpnStateService.VpnStateListener
{
private boolean mListening;
private VpnProfileDataSource mDataSource;
private VpnStateService mService;
private final ServiceConnection mServiceConnection = new ServiceConnection()
@ -53,7 +54,7 @@ public class VpnTileService extends TileService implements VpnStateService.VpnSt
public void onServiceConnected(ComponentName name, IBinder service)
{
mService = ((VpnStateService.LocalBinder)service).getService();
if (mDataSource != null)
if (mListening && mDataSource != null)
{
mService.registerListener(VpnTileService.this);
updateTile();
@ -69,6 +70,9 @@ public class VpnTileService extends TileService implements VpnStateService.VpnSt
Context context = getApplicationContext();
context.bindService(new Intent(context, VpnStateService.class),
mServiceConnection, Service.BIND_AUTO_CREATE);
mDataSource = new VpnProfileDataSource(this);
mDataSource.open();
}
@Override
@ -80,15 +84,15 @@ public class VpnTileService extends TileService implements VpnStateService.VpnSt
{
getApplicationContext().unbindService(mServiceConnection);
}
mDataSource.close();
mDataSource = null;
}
@Override
public void onStartListening()
{
super.onStartListening();
mDataSource = new VpnProfileDataSource(this);
mDataSource.open();
mListening = true;
if (mService != null)
{
@ -101,14 +105,12 @@ public class VpnTileService extends TileService implements VpnStateService.VpnSt
public void onStopListening()
{
super.onStopListening();
mListening = false;
if (mService != null)
{
mService.unregisterListener(this);
}
mDataSource.close();
mDataSource = null;
}
private VpnProfile getProfile()
@ -119,8 +121,7 @@ public class VpnTileService extends TileService implements VpnStateService.VpnSt
{
uuid = pref.getString(Constants.PREF_MRU_VPN_PROFILE, null);
}
return mDataSource.getVpnProfile(uuid);
return mDataSource != null ? mDataSource.getVpnProfile(uuid) : null;
}
@Override
@ -134,7 +135,7 @@ public class VpnTileService extends TileService implements VpnStateService.VpnSt
{
profile = getProfile();
}
else
else if (mDataSource != null)
{ /* always get the plain profile without cached password */
profile = mDataSource.getVpnProfile(profile.getId());
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016-2018 Tobias Brunner
* Copyright (C) 2016-2020 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@ -58,4 +58,9 @@ public final class Constants
* Preference key to store the most recently used VPN profile
*/
public static final String PREF_MRU_VPN_PROFILE = "pref_mru_vpn_profile";
/**
* Preference key to store whether the user permanently dismissed our warning to add the app to the power whitelist
*/
public static final String PREF_IGNORE_POWER_WHITELIST = "pref_ignore_power_whitelist";
}

View File

@ -9,6 +9,7 @@ backend/android_creds.c \
backend/android_fetcher.c \
backend/android_dns_proxy.c \
backend/android_private_key.c \
backend/android_scheduler.c \
backend/android_service.c \
charonservice.c \
kernel/android_ipsec.c \

View File

@ -58,6 +58,7 @@ typedef enum {
ANDROID_JELLY_BEAN_MR1 = 17,
ANDROID_JELLY_BEAN_MR2 = 18,
ANDROID_LOLLIPOP = 21,
ANDROID_MARSHMALLOW = 23,
} android_sdk_version_t;
/**

View File

@ -0,0 +1,330 @@
/*
* Copyright (C) 2020 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. *
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <sys/time.h>
#include "android_scheduler.h"
#include "../android_jni.h"
#include <collections/hashtable.h>
#include <processing/jobs/callback_job.h>
#include <threading/mutex.h>
/**
* Threshold in milliseconds up to which the default scheduler is used.
* This includes the roaming events (100 ms) and initial retransmits.
*/
#define DEFAULT_SCHEDULER_THRESHOLD 3000
typedef struct private_scheduler_t private_scheduler_t;
/**
* Private data.
*/
struct private_scheduler_t {
/**
* Public interface.
*/
scheduler_t public;
/**
* Reference to Scheduler object.
*/
jobject obj;
/**
* Java class for Scheduler.
*/
jclass cls;
/**
* Hashtable that stores scheduled jobs (entry_t*).
*/
hashtable_t *jobs;
/**
* Mutex to safely access the scheduled jobs.
*/
mutex_t *mutex;
/**
* Default scheduler used for short-term events.
*/
scheduler_t *default_scheduler;
};
/**
* Data for scheduled jobs.
*/
typedef struct {
/**
* Random identifier as string.
*/
char *id;
/**
* The scheduled job.
*/
job_t *job;
} entry_t;
CALLBACK(destroy_entry, void,
entry_t *this, const void *key)
{
DESTROY_IF(this->job);
free(this->id);
free(this);
}
JNI_METHOD(Scheduler, executeJob, void,
jstring jid)
{
private_scheduler_t *sched;
entry_t *entry;
char *id;
sched = (private_scheduler_t*)lib->scheduler;
id = androidjni_convert_jstring(env, jid);
sched->mutex->lock(sched->mutex);
entry = sched->jobs->remove(sched->jobs, id);
sched->mutex->unlock(sched->mutex);
free(id);
if (entry)
{
lib->processor->queue_job(lib->processor, entry->job);
entry->job = NULL;
destroy_entry(entry, NULL);
}
}
METHOD(scheduler_t, get_job_load, u_int,
private_scheduler_t *this)
{
u_int count;
this->mutex->lock(this->mutex);
count = this->jobs->get_count(this->jobs);
this->mutex->unlock(this->mutex);
return count;
}
/**
* Allocate an ID for a new job. We do this via JNI so we don't have to rely
* on RNGs being available when we replace the default scheduler.
*/
static jstring allocate_id(private_scheduler_t *this, JNIEnv *env)
{
jmethodID method_id;
method_id = (*env)->GetMethodID(env, this->cls, "allocateId",
"()Ljava/lang/String;");
if (!method_id)
{
return NULL;
}
return (*env)->CallObjectMethod(env, this->obj, method_id);
}
METHOD(scheduler_t, schedule_job_ms, void,
private_scheduler_t *this, job_t *job, uint32_t ms)
{
JNIEnv *env;
jmethodID method_id;
entry_t *entry = NULL;
jstring jid;
/* use the default scheduler for short-term events */
if (ms <= DEFAULT_SCHEDULER_THRESHOLD)
{
this->default_scheduler->schedule_job_ms(this->default_scheduler,
job, ms);
return;
}
androidjni_attach_thread(&env);
jid = allocate_id(this, env);
if (!jid)
{
goto failed;
}
method_id = (*env)->GetMethodID(env, this->cls, "scheduleJob",
"(Ljava/lang/String;J)V");
if (!method_id)
{
goto failed;
}
this->mutex->lock(this->mutex);
INIT(entry,
.id = androidjni_convert_jstring(env, jid),
.job = job,
);
job->status = JOB_STATUS_QUEUED;
this->jobs->put(this->jobs, entry->id, entry);
this->mutex->unlock(this->mutex);
(*env)->CallVoidMethod(env, this->obj, method_id, jid, (jlong)ms);
if (androidjni_exception_occurred(env))
{
goto failed;
}
androidjni_detach_thread();
return;
failed:
DBG1(DBG_JOB, "unable to schedule job for execution in %u ms", ms);
if (entry)
{
this->mutex->lock(this->mutex);
this->jobs->remove(this->jobs, entry->id);
this->mutex->unlock(this->mutex);
destroy_entry(entry, NULL);
}
else
{
job->destroy(job);
}
androidjni_exception_occurred(env);
androidjni_detach_thread();
}
METHOD(scheduler_t, schedule_job_tv, void,
private_scheduler_t *this, job_t *job, timeval_t tv)
{
timeval_t now;
time_monotonic(&now);
if (!timercmp(&now, &tv, <))
{
/* already expired, just send it to the processor */
lib->processor->queue_job(lib->processor, job);
return;
}
timersub(&tv, &now, &now);
schedule_job_ms(this, job, now.tv_sec * 1000 + now.tv_usec / 1000);
}
METHOD(scheduler_t, schedule_job, void,
private_scheduler_t *this, job_t *job, uint32_t s)
{
schedule_job_ms(this, job, s * 1000);
}
METHOD(scheduler_t, flush, void,
private_scheduler_t *this)
{
JNIEnv *env;
jmethodID method_id;
this->default_scheduler->flush(this->default_scheduler);
this->mutex->lock(this->mutex);
this->jobs->destroy_function(this->jobs, destroy_entry);
this->jobs = hashtable_create(hashtable_hash_str, hashtable_equals_str, 16);
this->mutex->unlock(this->mutex);
androidjni_attach_thread(&env);
method_id = (*env)->GetMethodID(env, this->cls, "Terminate", "()V");
if (!method_id)
{
androidjni_exception_occurred(env);
}
else
{
(*env)->CallVoidMethod(env, this->obj, method_id);
androidjni_exception_occurred(env);
}
androidjni_detach_thread();
}
METHOD(scheduler_t, destroy, void,
private_scheduler_t *this)
{
JNIEnv *env;
androidjni_attach_thread(&env);
if (this->obj)
{
(*env)->DeleteGlobalRef(env, this->obj);
}
if (this->cls)
{
(*env)->DeleteGlobalRef(env, this->cls);
}
androidjni_detach_thread();
this->default_scheduler->destroy(this->default_scheduler);
this->mutex->destroy(this->mutex);
this->jobs->destroy(this->jobs);
free(this);
}
/*
* Described in header
*/
scheduler_t *android_scheduler_create(jobject context, scheduler_t *scheduler)
{
private_scheduler_t *this;
JNIEnv *env;
jmethodID method_id;
jobject obj;
jclass cls;
INIT(this,
.public = {
.get_job_load = _get_job_load,
.schedule_job = _schedule_job,
.schedule_job_ms = _schedule_job_ms,
.schedule_job_tv = _schedule_job_tv,
.flush = _flush,
.destroy = _destroy,
},
.default_scheduler = scheduler,
.jobs = hashtable_create(hashtable_hash_str, hashtable_equals_str, 16),
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
);
androidjni_attach_thread(&env);
cls = (*env)->FindClass(env, JNI_PACKAGE_STRING "/Scheduler");
if (!cls)
{
goto failed;
}
this->cls = (*env)->NewGlobalRef(env, cls);
method_id = (*env)->GetMethodID(env, cls, "<init>",
"(Landroid/content/Context;)V");
if (!method_id)
{
goto failed;
}
obj = (*env)->NewObject(env, cls, method_id, context);
if (!obj)
{
goto failed;
}
this->obj = (*env)->NewGlobalRef(env, obj);
androidjni_detach_thread();
return &this->public;
failed:
DBG1(DBG_JOB, "failed to create Scheduler object");
androidjni_exception_occurred(env);
androidjni_detach_thread();
destroy(this);
return NULL;
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2020 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup android_scheduler android_scheduler
* @{ @ingroup android_backend
*/
#ifndef ANDROID_SCHEDULER_H_
#define ANDROID_SCHEDULER_H_
#include <jni.h>
#include <processing/scheduler.h>
/**
* Create an Android-specific scheduler_t implementation.
*
* The given scheduler is used for short-term events. We can't destroy it anyway
* because of the scheduler job operating on it, and this way we can use it to
* avoid the overhead of broadcasts for some events.
*
* @param context Context object
* @param scheduler the default scheduler used as fallback
* @return scheduler_t instance
*/
scheduler_t *android_scheduler_create(jobject context, scheduler_t *scheduler);
#endif /** ANDROID_SCHEDULER_H_ @}*/

View File

@ -754,13 +754,13 @@ static job_requeue_t initiate(private_android_service_t *this)
.unique = UNIQUE_REPLACE,
.rekey_time = 36000, /* 10h */
.jitter_time = 600, /* 10min */
.over_time = 600, /* 10min */
.over_time = 1800, /* 30min */
};
child_cfg_create_t child = {
.lifetime = {
.time = {
.life = 3600, /* 1h */
.rekey = 3000, /* 50min */
.life = 9000, /* 2.5h */
.rekey = 7200, /* 2h */
.jitter = 300 /* 5min */
},
},

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2019 Tobias Brunner
* Copyright (C) 2012-2020 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* HSR Hochschule fuer Technik Rapperswil
@ -27,6 +27,7 @@
#include "backend/android_creds.h"
#include "backend/android_fetcher.h"
#include "backend/android_private_key.h"
#include "backend/android_scheduler.h"
#include "backend/android_service.h"
#include "kernel/android_ipsec.h"
#include "kernel/android_net.h"
@ -45,6 +46,7 @@
#define ANDROID_RETRANSMIT_TIMEOUT 2.0
#define ANDROID_RETRANSMIT_BASE 1.4
#define ANDROID_KEEPALIVE_INTERVAL 45
#define ANDROID_KEEPALIVE_DPD_MARGIN 20
typedef struct private_charonservice_t private_charonservice_t;
@ -434,6 +436,10 @@ static void initiate(settings_t *settings)
"charon.keep_alive",
settings->get_int(settings, "global.nat_keepalive",
ANDROID_KEEPALIVE_INTERVAL));
/* send DPDs if above interval is exceeded, use a static value for now */
lib->settings->set_int(lib->settings,
"charon.keep_alive_dpd_margin",
ANDROID_KEEPALIVE_DPD_MARGIN);
/* reload plugins after changing settings */
lib->plugins->reload(lib->plugins, NULL);
@ -499,6 +505,8 @@ static void set_options(char *logfile)
"charon.initiator_only", TRUE);
lib->settings->set_bool(lib->settings,
"charon.close_ike_on_child_failure", TRUE);
lib->settings->set_bool(lib->settings,
"charon.check_current_path", TRUE);
/* setting the source address breaks the VpnService.protect() function which
* uses SO_BINDTODEVICE internally. the addresses provided to the kernel as
* auxiliary data have precedence over this option causing a routing loop if
@ -613,6 +621,14 @@ static void segv_handler(int signal)
exit(1);
}
/**
* Register this logger as default before we have the bus available
*/
static void __attribute__ ((constructor))register_logger()
{
dbg = dbg_android;
}
/**
* Initialize charon and the libraries via JNI
*/
@ -623,9 +639,6 @@ JNI_METHOD(CharonVpnService, initializeCharon, jboolean,
struct utsname utsname;
char *logfile, *appdir, *plugins;
/* logging for library during initialization, as we have no bus yet */
dbg = dbg_android;
/* initialize library */
if (!library_init(NULL, "charon"))
{
@ -633,6 +646,12 @@ JNI_METHOD(CharonVpnService, initializeCharon, jboolean,
return FALSE;
}
if (android_sdk_version >= ANDROID_MARSHMALLOW)
{
/* use a custom scheduler so the app is woken when jobs have to run */
lib->scheduler = android_scheduler_create(this, lib->scheduler);
}
/* set options before initializing other libraries that might read them */
logfile = androidjni_convert_jstring(env, jlogfile);
@ -743,8 +762,6 @@ JNI_METHOD_P(org_strongswan_android_utils, Utils, isProposalValid, jboolean,
char *str;
bool valid;
dbg = dbg_android;
if (!library_init(NULL, "charon"))
{
library_deinit();
@ -769,8 +786,6 @@ JNI_METHOD_P(org_strongswan_android_utils, Utils, parseInetAddressBytes, jbyteAr
host_t *host;
char *str;
dbg = dbg_android;
if (!library_init(NULL, "charon"))
{
library_deinit();

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 Tobias Brunner
HSR 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.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_launcher" />

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2012-2019 Tobias Brunner
Copyright (C) 2012-2020 Tobias Brunner
Copyright (C) 2012 Giuliano Grassi
Copyright (C) 2012 Ralf Sager
HSR Hochschule fuer Technik Rapperswil
@ -36,6 +36,8 @@
<string name="pref_title">Einstellungen</string>
<string name="pref_default_vpn_profile">Voreingestelltes VPN Profil</string>
<string name="pref_default_vpn_profile_mru">Verbinden mit zuletzt verwendetem Profil</string>
<string name="pref_power_whitelist_title">Akku-Optimierung ignorieren</string>
<string name="pref_power_whitelist_summary">Keine Warnung anzeigen, falls die App nicht auf der weissen Liste für Akku-Optimierung ist</string>
<!-- Log view -->
<string name="log_title">Log</string>
@ -215,6 +217,8 @@
<item quantity="other">Wiederholen in %1$d Sekunden</item>
</plurals>
<string name="cancel_retry">Wiederholen abbrechen</string>
<string name="power_whitelist_title">Akku-Optimierung deaktivieren</string>
<string name="power_whitelist_text">Bitte den nächsten Dialog bestätigen, um die App auf die weisse Liste für Akku-Optimierung zu setzen, so dass sie NAT keepalives und Rekeyings zeitlich korrekt planen kann, um konstant erreichbar zu bleiben während die VPN-Verbindung besteht.</string>
<!-- Quick Settings tile -->
<string name="tile_default">VPN umschalten</string>

View File

@ -36,6 +36,8 @@
<string name="pref_title">Settings</string>
<string name="pref_default_vpn_profile">Default VPN profile</string>
<string name="pref_default_vpn_profile_mru">Connect to most recently used profile</string>
<string name="pref_power_whitelist_title">Ignore battery optimizations</string>
<string name="pref_power_whitelist_summary">Don\'t show a warning if the app is not on the device\'s power whitelist</string>
<!-- Log view -->
<string name="log_title">Log</string>
@ -215,6 +217,8 @@
<item quantity="other">Retry in %1$d seconds</item>
</plurals>
<string name="cancel_retry">Cancel retry</string>
<string name="power_whitelist_title">Disable battery optimizations</string>
<string name="power_whitelist_text">Please confirm the next dialog to add the app to the device\'s power whitelist so it can ignore battery optimizations and schedule NAT keep-alives and rekeyings accurately in order to constantly keep reachable while the VPN is established.</string>
<!-- Quick Settings tile -->
<string name="tile_default">Toggle VPN</string>

View File

@ -33,6 +33,8 @@
<string name="pref_title">Settings</string>
<string name="pref_default_vpn_profile">Default VPN profile</string>
<string name="pref_default_vpn_profile_mru">Connect to most recently used profile</string>
<string name="pref_power_whitelist_title">Ignore battery optimizations</string>
<string name="pref_power_whitelist_summary">Don\'t show a warning if the app is not on the device\'s power whitelist</string>
<!-- Log view -->
<string name="log_title">Журнал</string>
@ -212,6 +214,8 @@
<item quantity="other">Retry in %1$d seconds</item>
</plurals>
<string name="cancel_retry">Cancel retry</string>
<string name="power_whitelist_title">Disable battery optimizations</string>
<string name="power_whitelist_text">Please confirm the next dialog to add the app to the device\'s power whitelist so it can ignore battery optimizations and schedule NAT keep-alives and rekeyings accurately in order to constantly keep reachable while the VPN is established.</string>
<!-- Quick Settings tile -->
<string name="tile_default">Toggle VPN</string>

View File

@ -34,6 +34,8 @@
<string name="pref_title">Settings</string>
<string name="pref_default_vpn_profile">Default VPN profile</string>
<string name="pref_default_vpn_profile_mru">Connect to most recently used profile</string>
<string name="pref_power_whitelist_title">Ignore battery optimizations</string>
<string name="pref_power_whitelist_summary">Don\'t show a warning if the app is not on the device\'s power whitelist</string>
<!-- Log view -->
<string name="log_title">Журнал</string>
@ -213,6 +215,8 @@
<item quantity="other">Retry in %1$d seconds</item>
</plurals>
<string name="cancel_retry">Cancel retry</string>
<string name="power_whitelist_title">Disable battery optimizations</string>
<string name="power_whitelist_text">Please confirm the next dialog to add the app to the device\'s power whitelist so it can ignore battery optimizations and schedule NAT keep-alives and rekeyings accurately in order to constantly keep reachable while the VPN is established.</string>
<!-- Quick Settings tile -->
<string name="tile_default">Toggle VPN</string>

View File

@ -33,6 +33,8 @@
<string name="pref_title">Settings</string>
<string name="pref_default_vpn_profile">Default VPN profile</string>
<string name="pref_default_vpn_profile_mru">Connect to most recently used profile</string>
<string name="pref_power_whitelist_title">Ignore battery optimizations</string>
<string name="pref_power_whitelist_summary">Don\'t show a warning if the app is not on the device\'s power whitelist</string>
<!-- Log view -->
<string name="log_title">日志</string>
@ -212,6 +214,8 @@
<item quantity="other">Retry in %1$d seconds</item>
</plurals>
<string name="cancel_retry">Cancel retry</string>
<string name="power_whitelist_title">Disable battery optimizations</string>
<string name="power_whitelist_text">Please confirm the next dialog to add the app to the device\'s power whitelist so it can ignore battery optimizations and schedule NAT keep-alives and rekeyings accurately in order to constantly keep reachable while the VPN is established.</string>
<!-- Quick Settings tile -->
<string name="tile_default">Toggle VPN</string>

View File

@ -33,6 +33,8 @@
<string name="pref_title">Settings</string>
<string name="pref_default_vpn_profile">Default VPN profile</string>
<string name="pref_default_vpn_profile_mru">Connect to most recently used profile</string>
<string name="pref_power_whitelist_title">Ignore battery optimizations</string>
<string name="pref_power_whitelist_summary">Don\'t show a warning if the app is not on the device\'s power whitelist</string>
<!-- Log view -->
<string name="log_title">日誌</string>
@ -212,6 +214,8 @@
<item quantity="other">Retry in %1$d seconds</item>
</plurals>
<string name="cancel_retry">Cancel retry</string>
<string name="power_whitelist_title">Disable battery optimizations</string>
<string name="power_whitelist_text">Please confirm the next dialog to add the app to the device\'s power whitelist so it can ignore battery optimizations and schedule NAT keep-alives and rekeyings accurately in order to constantly keep reachable while the VPN is established.</string>
<!-- Quick Settings tile -->
<string name="tile_default">Toggle VPN</string>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 Tobias Brunner
Copyright (C) 2020 Tobias Brunner
HSR Hochschule fuer Technik Rapperswil
This program is free software; you can redistribute it and/or modify it
@ -13,5 +13,7 @@
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_launcher" />
<resources>
<mipmap name="ic_launcher">@mipmap/ic_app</mipmap>
<mipmap name="ic_shortcut">@mipmap/ic_app</mipmap>
</resources>

View File

@ -36,6 +36,8 @@
<string name="pref_title">Settings</string>
<string name="pref_default_vpn_profile">Default VPN profile</string>
<string name="pref_default_vpn_profile_mru">Connect to most recently used profile</string>
<string name="pref_power_whitelist_title">Ignore battery optimizations</string>
<string name="pref_power_whitelist_summary">Don\'t show a warning if the app is not on the device\'s power whitelist</string>
<!-- Log view -->
<string name="log_title">Log</string>
@ -215,6 +217,8 @@
<item quantity="other">Retry in %1$d seconds</item>
</plurals>
<string name="cancel_retry">Cancel retry</string>
<string name="power_whitelist_title">Disable battery optimizations</string>
<string name="power_whitelist_text">Please confirm the next dialog to add the app to the device\'s power whitelist so it can ignore battery optimizations and schedule NAT keep-alives and rekeyings accurately in order to constantly keep reachable while the VPN is established.</string>
<!-- Quick Settings tile -->
<string name="tile_default">Toggle VPN</string>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 Tobias Brunner
Copyright (C) 2018-2020 Tobias Brunner
HSR Hochschule fuer Technik Rapperswil
This program is free software; you can redistribute it and/or modify it
@ -20,4 +20,9 @@
android:title="@string/pref_default_vpn_profile"
android:summary="@string/pref_default_vpn_profile_mru" />
<SwitchPreference
android:key="pref_ignore_power_whitelist"
android:title="@string/pref_power_whitelist_title"
android:summary="@string/pref_power_whitelist_summary" />
</PreferenceScreen>

View File

@ -4,7 +4,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.2'
classpath 'com.android.tools.build:gradle:3.6.3'
}
}

View File

@ -1,6 +1,6 @@
#Mon Oct 07 16:41:25 CEST 2019
#Wed Feb 26 10:22:42 CET 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

View File

@ -236,6 +236,12 @@ struct private_ike_sa_t {
*/
uint32_t keepalive_interval;
/**
* Time the NAT keep alive interval may be exceeded before triggering a DPD
* instead of a NAT keep alive
*/
uint32_t keepalive_dpd_margin;
/**
* The scheduled keep alive job, if any
*/
@ -655,7 +661,19 @@ METHOD(ike_sa_t, send_keepalive, void,
diff = now - last_out;
if (diff >= this->keepalive_interval)
if (this->keepalive_dpd_margin &&
diff > (this->keepalive_interval + this->keepalive_dpd_margin))
{
if (!this->task_manager->busy(this->task_manager))
{
DBG1(DBG_IKE, "sending DPD instead of keep alive %ds after last "
"outbound message", diff);
this->task_manager->queue_dpd(this->task_manager);
this->task_manager->initiate(this->task_manager);
}
diff = 0;
}
else if (diff >= this->keepalive_interval)
{
packet_t *packet;
chunk_t data;
@ -669,6 +687,7 @@ METHOD(ike_sa_t, send_keepalive, void,
packet->set_data(packet, data);
DBG1(DBG_IKE, "sending keep alive to %#H", this->other_host);
charon->sender->send_no_marker(charon->sender, packet);
this->stats[STAT_OUTBOUND] = now;
diff = 0;
}
if (!this->keepalive_job)
@ -2407,81 +2426,86 @@ METHOD(ike_sa_t, retransmit, status_t,
{
return INVALID_STATE;
}
this->stats[STAT_OUTBOUND] = time_monotonic(NULL);
if (this->task_manager->retransmit(this->task_manager, message_id) != SUCCESS)
switch (this->task_manager->retransmit(this->task_manager, message_id))
{
/* send a proper signal to brief interested bus listeners */
switch (this->state)
{
case IKE_CONNECTING:
{
/* retry IKE_SA_INIT/Main Mode if we have multiple keyingtries */
uint32_t tries = this->peer_cfg->get_keyingtries(this->peer_cfg);
charon->bus->alert(charon->bus, ALERT_PEER_INIT_UNREACHABLE,
this->keyingtry);
this->keyingtry++;
if (tries == 0 || tries > this->keyingtry)
{
DBG1(DBG_IKE, "peer not responding, trying again (%d/%d)",
this->keyingtry + 1, tries);
reset(this, TRUE);
resolve_hosts(this);
return this->task_manager->initiate(this->task_manager);
}
DBG1(DBG_IKE, "establishing IKE_SA failed, peer not responding");
if (this->version == IKEV1 && array_count(this->child_sas))
{
enumerator_t *enumerator;
child_sa_t *child_sa;
/* if reauthenticating an IKEv1 SA failed (assumed for an SA
* in this state with CHILD_SAs), try again from scratch */
DBG1(DBG_IKE, "reauthentication failed, trying to "
"reestablish IKE_SA");
reestablish(this);
/* trigger down events for the CHILD_SAs, as no down event
* is triggered below for IKE SAs in this state */
enumerator = array_create_enumerator(this->child_sas);
while (enumerator->enumerate(enumerator, &child_sa))
{
if (child_sa->get_state(child_sa) != CHILD_REKEYED &&
child_sa->get_state(child_sa) != CHILD_DELETED)
{
charon->bus->child_updown(charon->bus, child_sa,
FALSE);
}
}
enumerator->destroy(enumerator);
}
break;
}
case IKE_DELETING:
DBG1(DBG_IKE, "proper IKE_SA delete failed, peer not responding");
if (has_condition(this, COND_REAUTHENTICATING) &&
!lib->settings->get_bool(lib->settings,
"%s.make_before_break", FALSE, lib->ns))
{
DBG1(DBG_IKE, "delete during reauthentication failed, "
"trying to reestablish IKE_SA anyway");
reestablish(this);
}
break;
case IKE_REKEYING:
DBG1(DBG_IKE, "rekeying IKE_SA failed, peer not responding");
/* FALL */
default:
reestablish(this);
break;
}
if (this->state != IKE_CONNECTING &&
this->state != IKE_REKEYED)
{
charon->bus->ike_updown(charon->bus, &this->public, FALSE);
}
return DESTROY_ME;
case SUCCESS:
this->stats[STAT_OUTBOUND] = time_monotonic(NULL);
return SUCCESS;
case INVALID_STATE:
return INVALID_STATE;
default:
break;
}
return SUCCESS;
/* send a proper signal to brief interested bus listeners */
switch (this->state)
{
case IKE_CONNECTING:
{
/* retry IKE_SA_INIT/Main Mode if we have multiple keyingtries */
uint32_t tries = this->peer_cfg->get_keyingtries(this->peer_cfg);
charon->bus->alert(charon->bus, ALERT_PEER_INIT_UNREACHABLE,
this->keyingtry);
this->keyingtry++;
if (tries == 0 || tries > this->keyingtry)
{
DBG1(DBG_IKE, "peer not responding, trying again (%d/%d)",
this->keyingtry + 1, tries);
reset(this, TRUE);
resolve_hosts(this);
return this->task_manager->initiate(this->task_manager);
}
DBG1(DBG_IKE, "establishing IKE_SA failed, peer not responding");
if (this->version == IKEV1 && array_count(this->child_sas))
{
enumerator_t *enumerator;
child_sa_t *child_sa;
/* if reauthenticating an IKEv1 SA failed (assumed for an SA
* in this state with CHILD_SAs), try again from scratch */
DBG1(DBG_IKE, "reauthentication failed, trying to "
"reestablish IKE_SA");
reestablish(this);
/* trigger down events for the CHILD_SAs, as no down event
* is triggered below for IKE SAs in this state */
enumerator = array_create_enumerator(this->child_sas);
while (enumerator->enumerate(enumerator, &child_sa))
{
if (child_sa->get_state(child_sa) != CHILD_REKEYED &&
child_sa->get_state(child_sa) != CHILD_DELETED)
{
charon->bus->child_updown(charon->bus, child_sa,
FALSE);
}
}
enumerator->destroy(enumerator);
}
break;
}
case IKE_DELETING:
DBG1(DBG_IKE, "proper IKE_SA delete failed, peer not responding");
if (has_condition(this, COND_REAUTHENTICATING) &&
!lib->settings->get_bool(lib->settings,
"%s.make_before_break", FALSE, lib->ns))
{
DBG1(DBG_IKE, "delete during reauthentication failed, "
"trying to reestablish IKE_SA anyway");
reestablish(this);
}
break;
case IKE_REKEYING:
DBG1(DBG_IKE, "rekeying IKE_SA failed, peer not responding");
/* FALL */
default:
reestablish(this);
break;
}
if (this->state != IKE_CONNECTING &&
this->state != IKE_REKEYED)
{
charon->bus->ike_updown(charon->bus, &this->public, FALSE);
}
return DESTROY_ME;
}
METHOD(ike_sa_t, set_auth_lifetime, status_t,
@ -2689,6 +2713,14 @@ METHOD(ike_sa_t, roam, status_t,
this->task_manager->queue_mobike(this->task_manager, FALSE, TRUE);
return this->task_manager->initiate(this->task_manager);
}
if (lib->settings->get_bool(lib->settings,
"%s.check_current_path", FALSE, lib->ns) &&
!this->task_manager->busy(this->task_manager))
{
DBG1(DBG_IKE, "checking if current path still works using DPD");
this->task_manager->queue_dpd(this->task_manager);
return this->task_manager->initiate(this->task_manager);
}
return SUCCESS;
}
@ -3182,6 +3214,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
.unique_id = ref_get(&unique_id),
.keepalive_interval = lib->settings->get_time(lib->settings,
"%s.keep_alive", KEEPALIVE_INTERVAL, lib->ns),
.keepalive_dpd_margin = lib->settings->get_time(lib->settings,
"%s.keep_alive_dpd_margin", 0, lib->ns),
.retry_initiate_interval = lib->settings->get_time(lib->settings,
"%s.retry_initiate_interval", 0, lib->ns),
.flush_auth_cfg = lib->settings->get_bool(lib->settings,

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2006-2019 Tobias Brunner
* Copyright (C) 2006-2020 Tobias Brunner
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2009 Martin Willi
* Copyright (C) 2005 Jan Hutter
@ -872,10 +872,11 @@ struct ike_sa_t {
*
* @param message_id ID of the request to retransmit
* @return
* - SUCCESS
* - NOT_FOUND if request doesn't have to be retransmitted
* - SUCCESS if retransmit was sent
* - INVALID_STATE if no retransmit required
* - DESTROY_ME if this IKE_SA MUST be deleted
*/
status_t (*retransmit) (ike_sa_t *this, uint32_t message_id);
status_t (*retransmit)(ike_sa_t *this, uint32_t message_id);
/**
* Sends a DPD request to the peer.

View File

@ -404,7 +404,7 @@ static status_t retransmit_packet(private_task_manager_t *this, uint32_t seqnr,
METHOD(task_manager_t, retransmit, status_t,
private_task_manager_t *this, uint32_t seqnr)
{
status_t status = SUCCESS;
status_t status = INVALID_STATE;
if (seqnr == this->initiating.seqnr &&
array_count(this->initiating.packets))

View File

@ -409,7 +409,7 @@ METHOD(task_manager_t, retransmit, status_t,
"deferred");
this->ike_sa->set_condition(this->ike_sa, COND_STALE, TRUE);
this->initiating.deferred = TRUE;
return SUCCESS;
return INVALID_STATE;
}
else if (mobike->is_probing(mobike))
{
@ -443,7 +443,7 @@ METHOD(task_manager_t, retransmit, status_t,
"deferred");
this->ike_sa->set_condition(this->ike_sa, COND_STALE, TRUE);
this->initiating.deferred = TRUE;
return SUCCESS;
return INVALID_STATE;
}
}
@ -451,8 +451,9 @@ METHOD(task_manager_t, retransmit, status_t,
job = (job_t*)retransmit_job_create(this->initiating.mid,
this->ike_sa->get_id(this->ike_sa));
lib->scheduler->schedule_job_ms(lib->scheduler, job, timeout);
return SUCCESS;
}
return SUCCESS;
return INVALID_STATE;
}
METHOD(task_manager_t, initiate, status_t,

View File

@ -93,30 +93,6 @@ struct private_scheduler_t {
condvar_t *condvar;
};
/**
* Compares two timevals, return >0 if a > b, <0 if a < b and =0 if equal
*/
static int timeval_cmp(timeval_t *a, timeval_t *b)
{
if (a->tv_sec > b->tv_sec)
{
return 1;
}
if (a->tv_sec < b->tv_sec)
{
return -1;
}
if (a->tv_usec > b->tv_usec)
{
return 1;
}
if (a->tv_usec < b->tv_usec)
{
return -1;
}
return 0;
}
/**
* Returns the top event without removing it. Returns NULL if the heap is empty.
*/
@ -132,6 +108,7 @@ static event_t *peek_event(private_scheduler_t *this)
static event_t *remove_event(private_scheduler_t *this)
{
event_t *event, *top;
if (!this->event_count)
{
return NULL;
@ -144,21 +121,22 @@ static event_t *remove_event(private_scheduler_t *this)
if (--this->event_count > 1)
{
/* seep down the top event */
u_int position = 1;
/* seep down the top event */
while ((position << 1) <= this->event_count)
{
u_int child = position << 1;
if ((child + 1) <= this->event_count &&
timeval_cmp(&this->heap[child + 1]->time,
&this->heap[child]->time) < 0)
timercmp(&this->heap[child + 1]->time,
&this->heap[child]->time, <))
{
/* the "right" child is smaller */
child++;
}
if (timeval_cmp(&top->time, &this->heap[child]->time) <= 0)
if (!timercmp(&top->time, &this->heap[child]->time, >))
{
/* the top event fires before the smaller of the two children,
* stop */
@ -189,7 +167,7 @@ static job_requeue_t schedule(private_scheduler_t * this)
if ((event = peek_event(this)) != NULL)
{
if (timeval_cmp(&now, &event->time) >= 0)
if (!timercmp(&now, &event->time, <))
{
remove_event(this);
this->mutex->unlock(this->mutex);
@ -231,6 +209,7 @@ METHOD(scheduler_t, get_job_load, u_int,
private_scheduler_t *this)
{
int count;
this->mutex->lock(this->mutex);
count = this->event_count;
this->mutex->unlock(this->mutex);
@ -262,8 +241,8 @@ METHOD(scheduler_t, schedule_job_tv, void,
position = this->event_count;
/* then bubble it up */
while (position > 1 && timeval_cmp(&this->heap[position >> 1]->time,
&event->time) > 0)
while (position > 1 &&
timercmp(&this->heap[position >> 1]->time, &event->time, >))
{
/* parent has to be fired after the new event, move up */
this->heap[position] = this->heap[position >> 1];
@ -354,4 +333,3 @@ scheduler_t * scheduler_create()
return &this->public;
}

View File

@ -239,7 +239,8 @@ METHOD(condvar_t, wait_, void,
}
/* use the monotonic clock based version of this function if available */
#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) && \
!defined(HAVE_CONDATTR_CLOCK_MONOTONIC)
#define pthread_cond_timedwait pthread_cond_timedwait_monotonic
#endif
@ -336,7 +337,7 @@ condvar_t *condvar_create(condvar_type_t type)
pthread_condattr_t condattr;
pthread_condattr_init(&condattr);
#ifdef HAVE_CONDATTR_CLOCK_MONOTONIC
pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
pthread_condattr_setclock(&condattr, TIME_CLOCK_ID);
#endif
pthread_cond_init(&this->condvar, &condattr);
pthread_condattr_destroy(&condattr);

View File

@ -37,6 +37,9 @@
#define HAVE_PTHREAD_CONDATTR_INIT 1
#define HAVE_CONDATTR_CLOCK_MONOTONIC 1
#undef TIME_CLOCK_ID
#define TIME_CLOCK_ID CLOCK_REALTIME
#define HAVE_SYS_CAPABILITY_H 1
#else /* __ANDROID_API__ */

View File

@ -52,7 +52,7 @@ time_t time_monotonic(timeval_t *tv)
* monotonic time source only if it is also supported by pthread. */
timespec_t ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
if (clock_gettime(TIME_CLOCK_ID, &ts) == 0)
{
if (tv)
{

View File

@ -32,6 +32,20 @@
*/
#define TIME_32_BIT_SIGNED_MAX 0x7fffffff
/**
* The clock that should be used for time_monotonic() and conditional variables
*/
#ifdef HAVE_CLOCK_GETTIME
#ifdef HAVE_CONDATTR_CLOCK_MONOTONIC
/* only can use different clocks if we can set it via attribute */
#ifndef TIME_CLOCK_ID
#define TIME_CLOCK_ID CLOCK_MONOTONIC
#endif
#else
#define TIME_CLOCK_ID CLOCK_MONOTONIC
#endif
#endif
/**
* Handle struct timeval like an own type.
*/