Guard Android VPN runtime generations
This commit is contained in:
@@ -30,8 +30,8 @@ android {
|
|||||||
applicationId "su.cin.rapvpn"
|
applicationId "su.cin.rapvpn"
|
||||||
minSdk 26
|
minSdk 26
|
||||||
targetSdk 35
|
targetSdk 35
|
||||||
versionCode 200
|
versionCode 201
|
||||||
versionName "0.2.200"
|
versionName "0.2.201"
|
||||||
buildConfigField "String", "DEFAULT_BACKEND_URL", "\"${normalizeGradleString(defaultBackendUrl)}\""
|
buildConfigField "String", "DEFAULT_BACKEND_URL", "\"${normalizeGradleString(defaultBackendUrl)}\""
|
||||||
buildConfigField "String", "DEFAULT_CLUSTER_ID", "\"${normalizeGradleString(defaultClusterId)}\""
|
buildConfigField "String", "DEFAULT_CLUSTER_ID", "\"${normalizeGradleString(defaultClusterId)}\""
|
||||||
buildConfigField "String", "DEFAULT_ORGANIZATION_ID", "\"${normalizeGradleString(defaultOrganizationId)}\""
|
buildConfigField "String", "DEFAULT_ORGANIZATION_ID", "\"${normalizeGradleString(defaultOrganizationId)}\""
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ public class RapVpnService extends VpnService {
|
|||||||
private final AtomicLong runtimeWatchdogRecoveries = new AtomicLong();
|
private final AtomicLong runtimeWatchdogRecoveries = new AtomicLong();
|
||||||
private final AtomicLong tcpHandshakeStalls = new AtomicLong();
|
private final AtomicLong tcpHandshakeStalls = new AtomicLong();
|
||||||
private final AtomicLong runtimeWatchdogHardRestarts = new AtomicLong();
|
private final AtomicLong runtimeWatchdogHardRestarts = new AtomicLong();
|
||||||
|
private final AtomicLong runtimeGeneration = new AtomicLong();
|
||||||
private final AtomicLong diagnosticEnsureRequests = new AtomicLong();
|
private final AtomicLong diagnosticEnsureRequests = new AtomicLong();
|
||||||
private final AtomicLong diagnosticRestartRequests = new AtomicLong();
|
private final AtomicLong diagnosticRestartRequests = new AtomicLong();
|
||||||
private final AtomicBoolean hardRuntimeRestartInProgress = new AtomicBoolean();
|
private final AtomicBoolean hardRuntimeRestartInProgress = new AtomicBoolean();
|
||||||
@@ -1025,6 +1026,7 @@ public class RapVpnService extends VpnService {
|
|||||||
writeActivePacketRelayConfig(selectedRelayUrl, relayUrls);
|
writeActivePacketRelayConfig(selectedRelayUrl, relayUrls);
|
||||||
stopPacketRelay();
|
stopPacketRelay();
|
||||||
running = true;
|
running = true;
|
||||||
|
long runtimeId = runtimeGeneration.incrementAndGet();
|
||||||
runtimeStartedAt = System.currentTimeMillis();
|
runtimeStartedAt = System.currentTimeMillis();
|
||||||
uplinkWorkerCount = Math.max(1, Math.min(UPLINK_WORKER_MAX_COUNT, Math.max(1, Runtime.getRuntime().availableProcessors() - 1)));
|
uplinkWorkerCount = Math.max(1, Math.min(UPLINK_WORKER_MAX_COUNT, Math.max(1, Runtime.getRuntime().availableProcessors() - 1)));
|
||||||
if (uplinkWorkerCount < 2) {
|
if (uplinkWorkerCount < 2) {
|
||||||
@@ -1073,15 +1075,15 @@ public class RapVpnService extends VpnService {
|
|||||||
writeRuntimeStatus("relay_reset_warning", "queue reset failed: " + e.getMessage(), 0, 0, 0, 1);
|
writeRuntimeStatus("relay_reset_warning", "queue reset failed: " + e.getMessage(), 0, 0, 0, 1);
|
||||||
}
|
}
|
||||||
}, "rap-vpn-relay-reset");
|
}, "rap-vpn-relay-reset");
|
||||||
uplinkThread = new Thread(() -> pumpTunToRelay(clusterId, vpnConnectionId), "rap-vpn-uplink");
|
uplinkThread = new Thread(() -> pumpTunToRelay(runtimeId, clusterId, vpnConnectionId), "rap-vpn-uplink");
|
||||||
uplinkSenderThreads = new Thread[uplinkWorkerCount];
|
uplinkSenderThreads = new Thread[uplinkWorkerCount];
|
||||||
for (int i = 0; i < uplinkWorkerCount; i++) {
|
for (int i = 0; i < uplinkWorkerCount; i++) {
|
||||||
final int workerIndex = i;
|
final int workerIndex = i;
|
||||||
uplinkSenderThreads[i] = new Thread(() -> pumpUplinkQueueToRelay(workerIndex, clusterId, vpnConnectionId), "rap-vpn-uplink-sender-" + workerIndex);
|
uplinkSenderThreads[i] = new Thread(() -> pumpUplinkQueueToRelay(runtimeId, workerIndex, clusterId, vpnConnectionId), "rap-vpn-uplink-sender-" + workerIndex);
|
||||||
}
|
}
|
||||||
downlinkThread = new Thread(() -> runDownlinkWithRestart(clusterId, vpnConnectionId), "rap-vpn-downlink-receiver");
|
downlinkThread = new Thread(() -> runDownlinkWithRestart(runtimeId, clusterId, vpnConnectionId), "rap-vpn-downlink-receiver");
|
||||||
downlinkWriterThread = new Thread(this::pumpDownlinkQueueToTun, "rap-vpn-downlink-writer");
|
downlinkWriterThread = new Thread(() -> pumpDownlinkQueueToTun(runtimeId), "rap-vpn-downlink-writer");
|
||||||
runtimeWatchdogThread = new Thread(() -> runRuntimeWatchdog(clusterId, vpnConnectionId), "rap-vpn-runtime-watchdog");
|
runtimeWatchdogThread = new Thread(() -> runRuntimeWatchdog(runtimeId, clusterId, vpnConnectionId), "rap-vpn-runtime-watchdog");
|
||||||
diagnosticWatchdogThread = new Thread(this::runDiagnosticServiceWatchdog, "rap-vpn-diagnostic-watchdog");
|
diagnosticWatchdogThread = new Thread(this::runDiagnosticServiceWatchdog, "rap-vpn-diagnostic-watchdog");
|
||||||
if (resetThread != null) {
|
if (resetThread != null) {
|
||||||
resetThread.start();
|
resetThread.start();
|
||||||
@@ -1308,6 +1310,7 @@ public class RapVpnService extends VpnService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void stopPacketRelay() {
|
private void stopPacketRelay() {
|
||||||
|
runtimeGeneration.incrementAndGet();
|
||||||
running = false;
|
running = false;
|
||||||
VpnPacketWebSocketRelay relay = packetWebSocketRelay;
|
VpnPacketWebSocketRelay relay = packetWebSocketRelay;
|
||||||
packetWebSocketRelay = null;
|
packetWebSocketRelay = null;
|
||||||
@@ -1564,13 +1567,17 @@ public class RapVpnService extends VpnService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runDownlinkWithRestart(String clusterId, String vpnConnectionId) {
|
private boolean isRuntimeActive(long runtimeId) {
|
||||||
|
return running && runtimeGeneration.get() == runtimeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runDownlinkWithRestart(long runtimeId, String clusterId, String vpnConnectionId) {
|
||||||
long restarts = 0;
|
long restarts = 0;
|
||||||
while (running) {
|
while (isRuntimeActive(runtimeId)) {
|
||||||
downlinkRestarts = restarts;
|
downlinkRestarts = restarts;
|
||||||
writeRuntimeDetail("starting", "downlink loop starting", "downlink", 0, restarts, "");
|
writeRuntimeDetail("starting", "downlink loop starting", "downlink", 0, restarts, "");
|
||||||
pumpRelayToTun(clusterId, vpnConnectionId);
|
pumpRelayToTun(clusterId, vpnConnectionId);
|
||||||
if (!running) {
|
if (!isRuntimeActive(runtimeId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
restarts++;
|
restarts++;
|
||||||
@@ -1580,18 +1587,18 @@ public class RapVpnService extends VpnService {
|
|||||||
try {
|
try {
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
if (!running) {
|
if (!isRuntimeActive(runtimeId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runRuntimeWatchdog(String clusterId, String vpnConnectionId) {
|
private void runRuntimeWatchdog(long runtimeId, String clusterId, String vpnConnectionId) {
|
||||||
while (running) {
|
while (isRuntimeActive(runtimeId)) {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(RUNTIME_WATCHDOG_INTERVAL_MS);
|
Thread.sleep(RUNTIME_WATCHDOG_INTERVAL_MS);
|
||||||
if (!running) {
|
if (!isRuntimeActive(runtimeId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
@@ -1645,7 +1652,7 @@ public class RapVpnService extends VpnService {
|
|||||||
recoverPacketRelayRuntime(clusterId, vpnConnectionId, "tcp_handshake_stall stale=" + stale);
|
recoverPacketRelayRuntime(clusterId, vpnConnectionId, "tcp_handshake_stall stale=" + stale);
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
if (!running) {
|
if (!isRuntimeActive(runtimeId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -1821,7 +1828,7 @@ public class RapVpnService extends VpnService {
|
|||||||
return value == null || value.isEmpty() ? "missing" : "present";
|
return value == null || value.isEmpty() ? "missing" : "present";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pumpTunToRelay(String clusterId, String vpnConnectionId) {
|
private void pumpTunToRelay(long runtimeId, String clusterId, String vpnConnectionId) {
|
||||||
byte[] packet = new byte[32767];
|
byte[] packet = new byte[32767];
|
||||||
long readPackets = 0;
|
long readPackets = 0;
|
||||||
FileDescriptor fd = null;
|
FileDescriptor fd = null;
|
||||||
@@ -1831,7 +1838,7 @@ public class RapVpnService extends VpnService {
|
|||||||
input = new FileInputStream(fd);
|
input = new FileInputStream(fd);
|
||||||
uplinkTunFd = fd;
|
uplinkTunFd = fd;
|
||||||
uplinkTunInput = input;
|
uplinkTunInput = input;
|
||||||
while (running) {
|
while (isRuntimeActive(runtimeId)) {
|
||||||
int n = input.read(packet);
|
int n = input.read(packet);
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
readPackets++;
|
readPackets++;
|
||||||
@@ -1849,7 +1856,7 @@ public class RapVpnService extends VpnService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (running) {
|
if (isRuntimeActive(runtimeId)) {
|
||||||
Log.e(TAG, "vpn uplink stopped", e);
|
Log.e(TAG, "vpn uplink stopped", e);
|
||||||
writeRuntimeStatus("error", "uplink stopped: " + e.getMessage(), readPackets, 0, 0, 0);
|
writeRuntimeStatus("error", "uplink stopped: " + e.getMessage(), readPackets, 0, 0, 0);
|
||||||
writeRuntimeDetail("stopped", "uplink stopped: " + e.getMessage(), "uplink", readPackets, 0, e.getClass().getSimpleName());
|
writeRuntimeDetail("stopped", "uplink stopped: " + e.getMessage(), "uplink", readPackets, 0, e.getClass().getSimpleName());
|
||||||
@@ -2474,11 +2481,11 @@ public class RapVpnService extends VpnService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pumpUplinkQueueToRelay(int workerIndex, String clusterId, String vpnConnectionId) {
|
private void pumpUplinkQueueToRelay(long runtimeId, int workerIndex, String clusterId, String vpnConnectionId) {
|
||||||
long sentPackets = 0;
|
long sentPackets = 0;
|
||||||
long errors = 0;
|
long errors = 0;
|
||||||
List<byte[]> batch = new ArrayList<>(VPN_BATCH_MAX_PACKETS);
|
List<byte[]> batch = new ArrayList<>(VPN_BATCH_MAX_PACKETS);
|
||||||
while (running) {
|
while (isRuntimeActive(runtimeId)) {
|
||||||
try {
|
try {
|
||||||
BlockingQueue<byte[]>[] queues = uplinkQueues;
|
BlockingQueue<byte[]>[] queues = uplinkQueues;
|
||||||
if (queues == null || workerIndex < 0 || workerIndex >= queues.length) {
|
if (queues == null || workerIndex < 0 || workerIndex >= queues.length) {
|
||||||
@@ -2556,12 +2563,12 @@ public class RapVpnService extends VpnService {
|
|||||||
writeRuntimeStatus("uplink_sent", "sent batch=" + batch.size(), 0, sentPackets, 0, errors);
|
writeRuntimeStatus("uplink_sent", "sent batch=" + batch.size(), 0, sentPackets, 0, errors);
|
||||||
writeRuntimeDetail("sent", "worker=" + workerIndex + " sent batch=" + batch.size(), "uplink_sender", sentPackets, errors, "", workerIndex);
|
writeRuntimeDetail("sent", "worker=" + workerIndex + " sent batch=" + batch.size(), "uplink_sender", sentPackets, errors, "", workerIndex);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
if (!running) {
|
if (!isRuntimeActive(runtimeId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
writeRuntimeDetail("read_wait", "uplink queue wait interrupted", "uplink_sender", sentPackets, errors, e.getClass().getSimpleName(), workerIndex);
|
writeRuntimeDetail("read_wait", "uplink queue wait interrupted", "uplink_sender", sentPackets, errors, e.getClass().getSimpleName(), workerIndex);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (running) {
|
if (isRuntimeActive(runtimeId)) {
|
||||||
Log.w(TAG, "vpn uplink batch send failed; continuing", e);
|
Log.w(TAG, "vpn uplink batch send failed; continuing", e);
|
||||||
errors++;
|
errors++;
|
||||||
AtomicLong[] senderErrors = uplinkSenderErrorsByWorker;
|
AtomicLong[] senderErrors = uplinkSenderErrorsByWorker;
|
||||||
@@ -2573,7 +2580,7 @@ public class RapVpnService extends VpnService {
|
|||||||
try {
|
try {
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
} catch (InterruptedException interrupted) {
|
} catch (InterruptedException interrupted) {
|
||||||
if (!running) {
|
if (!isRuntimeActive(runtimeId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3191,7 +3198,7 @@ public class RapVpnService extends VpnService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pumpDownlinkQueueToTun() {
|
private void pumpDownlinkQueueToTun(long runtimeId) {
|
||||||
long writtenPackets = 0;
|
long writtenPackets = 0;
|
||||||
long errors = 0;
|
long errors = 0;
|
||||||
FileDescriptor fd = null;
|
FileDescriptor fd = null;
|
||||||
@@ -3199,7 +3206,7 @@ public class RapVpnService extends VpnService {
|
|||||||
try {
|
try {
|
||||||
fd = Os.dup(tunnel.getFileDescriptor());
|
fd = Os.dup(tunnel.getFileDescriptor());
|
||||||
downlinkTunFd = fd;
|
downlinkTunFd = fd;
|
||||||
while (running) {
|
while (isRuntimeActive(runtimeId)) {
|
||||||
BlockingQueue<byte[]>[] queues = downlinkQueues;
|
BlockingQueue<byte[]>[] queues = downlinkQueues;
|
||||||
if (queues == null || queues.length == 0) {
|
if (queues == null || queues.length == 0) {
|
||||||
return;
|
return;
|
||||||
@@ -3228,7 +3235,7 @@ public class RapVpnService extends VpnService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (running) {
|
if (isRuntimeActive(runtimeId)) {
|
||||||
Log.e(TAG, "vpn downlink writer stopped", e);
|
Log.e(TAG, "vpn downlink writer stopped", e);
|
||||||
writeRuntimeStatus("error", "downlink writer stopped: " + e.getMessage(), 0, 0, writtenPackets, errors);
|
writeRuntimeStatus("error", "downlink writer stopped: " + e.getMessage(), 0, 0, writtenPackets, errors);
|
||||||
writeRuntimeDetail("stopped", "downlink writer stopped: " + e.getMessage(), "downlink_writer", writtenPackets, errors, e.getClass().getSimpleName());
|
writeRuntimeDetail("stopped", "downlink writer stopped: " + e.getMessage(), "downlink_writer", writtenPackets, errors, e.getClass().getSimpleName());
|
||||||
|
|||||||
Reference in New Issue
Block a user