summaryrefslogtreecommitdiffhomepage
path: root/tunnel
diff options
context:
space:
mode:
authorMikael Magnusson <mikma@users.sourceforge.net>2023-06-02 22:50:10 +0200
committerMikael Magnusson <mikma@users.sourceforge.net>2023-10-26 23:10:49 +0200
commit7f58506d3eaff8c26fad336a6fc3caa97e287dd3 (patch)
tree839db63a8b122f95f8d0e1cd0f5a277e92160c2e /tunnel
parent5f6e909e610147c9f0d38e72457c79896c3e3e04 (diff)
tunnel: renew DHCP leases
* Add valid and preferred lifetime to dhcp leases * Delay renew until VPN network is available
Diffstat (limited to 'tunnel')
-rw-r--r--tunnel/src/main/java/com/wireguard/android/backend/Dhcp.java45
-rw-r--r--tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java121
-rw-r--r--tunnel/tools/libwg-go/dhcp.go9
3 files changed, 152 insertions, 23 deletions
diff --git a/tunnel/src/main/java/com/wireguard/android/backend/Dhcp.java b/tunnel/src/main/java/com/wireguard/android/backend/Dhcp.java
index 59a3e69c..41060ddf 100644
--- a/tunnel/src/main/java/com/wireguard/android/backend/Dhcp.java
+++ b/tunnel/src/main/java/com/wireguard/android/backend/Dhcp.java
@@ -8,6 +8,11 @@ package com.wireguard.android.backend;
import com.wireguard.config.InetNetwork;
import com.wireguard.util.NonNullForAll;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.util.LinkedHashSet;
import java.util.Set;
/**
@@ -15,14 +20,44 @@ import java.util.Set;
*/
@NonNullForAll
public class Dhcp {
- private Set<InetNetwork> addresses;
+ private Set<Lease> leases = new LinkedHashSet<>();
- Dhcp(Set<InetNetwork> addresses) {
- this.addresses = addresses;
+ public class Lease {
+ private InetNetwork address;
+ private Duration validLt;
+ private Duration preferredLt;
+ private Instant validTs;
+ private Instant preferredTs;
+
+ public Lease(InetNetwork address, Duration validLt, Duration preferredLt) {
+ this.address = address;
+ this.validLt = validLt;
+ this.preferredLt = preferredLt;
+
+ Instant now = Instant.now();
+ this.validTs = now.plus(validLt);
+ this.preferredTs = now.plus(preferredLt);
+ }
+
+ public final InetNetwork getAddress() {
+ return this.address;
+ }
+
+ public String toString() {
+ ZoneId zone = ZoneId.systemDefault();
+ LocalTime validLocal = validTs.atZone(zone).toLocalTime().withNano(0);
+ LocalTime preferredLocal = preferredTs.atZone(zone).toLocalTime().withNano(0);
+ // TODO add date when needed
+ return address.toString() + " (valid " + validLocal + ", preferred " + preferredLocal + ")";
+ }
+ }
+
+ public void addLease(InetNetwork address, Duration valid, Duration preferred) {
+ this.leases.add(new Lease(address, valid, preferred));
}
- public Set<InetNetwork> getAddresses() {
- return addresses;
+ public final Set<Lease> getLeases() {
+ return leases;
}
public String toString() {
diff --git a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
index 6bd42936..79b724e8 100644
--- a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
+++ b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
@@ -5,6 +5,7 @@
package com.wireguard.android.backend;
+import android.app.AlarmManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -18,8 +19,12 @@ import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.SystemClock;
import android.system.OsConstants;
import android.util.Log;
@@ -75,10 +80,10 @@ import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.net.URL;
+import java.time.Duration;
import java.time.Instant;
import java.nio.ByteOrder;
import java.util.Collections;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -104,6 +109,7 @@ public final class GoBackend implements Backend {
private static final int DNS_RESOLUTION_RETRIES = 10;
private static final String TAG = "WireGuard/GoBackend";
private static final int STATS_TAG = 2;
+ private static final int MSG_DHCP_EXPIRE = 1;
@Nullable private static AlwaysOnCallback alwaysOnCallback;
private static GhettoCompletableFuture<VpnService> vpnService = new GhettoCompletableFuture<>();
private final Context context;
@@ -117,6 +123,8 @@ public final class GoBackend implements Backend {
private ManagedChannel channel;
private boolean obtainDhcpLease = false;
@Nullable private Bgp bgp;
+ private HandlerThread thread;
+ private Handler handler;
/**
* Public constructor for GoBackend.
@@ -398,6 +406,10 @@ public final class GoBackend implements Backend {
}
private void Dhcp(VpnService service) throws Exception{
+ if (currentConfig == null || currentTunnel == null || currentTunnelHandle < 0) {
+ return;
+ }
+
obtainDhcpLease = false;
// Heuristics: Use first ULA address as client address
@@ -421,23 +433,27 @@ public final class GoBackend implements Backend {
DhcpResponse resp = stub.dhcp(request);
Log.i(TAG, "Dhcp: " + resp.getError().getMessage());
- Set<InetNetwork> addresses = new LinkedHashSet<>();
+ Dhcp dhcp = new Dhcp();
+ long delayMillis = 1000 * 3600 * 12; // Max renew 12h
+
if (resp.getLeasesList() != null) {
for (final Lease lease: resp.getLeasesList()) {
try {
InetAddress addr = InetAddress.getByAddress(lease.getAddress().getAddress().toByteArray());
- Log.i(TAG, "Lease: " + addr);
- addresses.add(new InetNetwork(addr, 128));
+ Log.i(TAG, "Lease: " + addr + " " + lease.getValidLifetime().getSeconds() + " " + lease.getPreferredLifetime().getSeconds());
+ dhcp.addLease(new InetNetwork(addr, 128),
+ Duration.ofSeconds(lease.getValidLifetime().getSeconds(), lease.getValidLifetime().getNanos()),
+ Duration.ofSeconds(lease.getPreferredLifetime().getSeconds(), lease.getPreferredLifetime().getNanos()));
+ long leaseDelayMillis = lease.getValidLifetime().getSeconds() * 1000 + lease.getValidLifetime().getNanos() / 1000;
+ delayMillis = Math.min(delayMillis, leaseDelayMillis);
} catch (UnknownHostException ex) {
// Ignore
}
}
}
- Dhcp dhcp = new Dhcp(addresses);
-
// Replace the vpn tunnel
- final VpnService.Builder builder = getBuilder(currentTunnel.getName(), currentConfig, service, dhcp.getAddresses());
+ final VpnService.Builder builder = getBuilder(currentTunnel.getName(), currentConfig, service, dhcp.getLeases());
Log.i(TAG, "Builder: " + builder);
@@ -455,8 +471,14 @@ public final class GoBackend implements Backend {
service.protect(wgGetSocketV6(currentTunnelHandle));
Log.i(TAG, "Dhcp done");
- bgp = new Bgp(this, channel, currentTunnel, currentTunnelHandle);
- bgp.startServer();
+ AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
+ am.cancel(alarmListener);
+ am.setWindow(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + delayMillis * 3 / 4, delayMillis / 4, null, alarmListener, handler);
+
+ if (bgp != null) {
+ bgp = new Bgp(this, channel, currentTunnel, currentTunnelHandle);
+ bgp.startServer();
+ }
currentTunnel.onDhcpChange(dhcp);
}
@@ -543,7 +565,7 @@ public final class GoBackend implements Backend {
Log.i(TAG, "Exit streamReverse");
}
- private VpnService.Builder getBuilder(final String name, @Nullable final Config config, final VpnService service, @Nullable final Set<InetNetwork> leases) throws PackageManager.NameNotFoundException {
+ private VpnService.Builder getBuilder(final String name, @Nullable final Config config, final VpnService service, @Nullable final Set<Dhcp.Lease> leases) throws PackageManager.NameNotFoundException {
Log.i(TAG, "Builder 1");
final VpnService.Builder builder = service.getBuilder();
Log.i(TAG, "Builder 2");
@@ -559,8 +581,9 @@ public final class GoBackend implements Backend {
Log.i(TAG, "Builder 5");
if (leases != null) {
- for (final InetNetwork lease: leases) {
- builder.addAddress(lease.getAddress(), lease.getMask());
+ for (final Dhcp.Lease lease: leases) {
+ InetNetwork addr = lease.getAddress();
+ builder.addAddress(addr.getAddress(), addr.getMask());
}
}
@@ -659,7 +682,11 @@ public final class GoBackend implements Backend {
activeNetwork = connectivityManager.getActiveNetwork();
- if (!connectivityManager.getNetworkCapabilities(activeNetwork).hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)) {
+ if (activeNetwork == null) {
+ Log.w(TAG, "Null activeNetwork");
+ activeNetwork = null;
+ }
+ else if (!connectivityManager.getNetworkCapabilities(activeNetwork).hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)) {
Log.w(TAG, "VPN network is active, null activeNetwork");
activeNetwork = null;
}
@@ -708,6 +735,33 @@ public final class GoBackend implements Backend {
obtainDhcpLease = true;
+ thread = new HandlerThread("GoBackend HandlerThread");
+ thread.start();
+ handler = new Handler(thread.getLooper()) {
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_DHCP_EXPIRE:
+ Log.w(TAG, "DHCP expire: " + ((activeNetwork != null) ? activeNetwork : "disabled"));
+ try {
+ if (activeNetwork != null) {
+ Log.w(TAG, "DHCP before");
+ Dhcp(service); // Renew addresses
+ Log.w(TAG, "DHCP after");
+ } else {
+ Log.w(TAG, "DHCP delay obtain lease");
+ obtainDhcpLease = true;
+ }
+ } catch (Exception ex) {
+ Log.e(TAG, "DHCP failed: " + ex);
+ }
+ break;
+ default:
+ Log.w(TAG, "Unknown message: " + msg.what);
+ break;
+ }
+ }
+ };
+
NetworkRequest req = new NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN).build();
connectivityManager.requestNetwork(req, myNetworkCallback);
@@ -723,6 +777,14 @@ public final class GoBackend implements Backend {
currentTunnel = null;
currentTunnelHandle = -1;
currentConfig = null;
+
+ AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
+ am.cancel(alarmListener);
+ if (thread != null) {
+ handler = null;
+ thread.quit();
+ thread = null;
+ }
if (bgp != null) {
bgp.stopServer();
bgp = null;
@@ -802,6 +864,14 @@ public final class GoBackend implements Backend {
owner.bgp.stopServer();
owner.bgp = null;
}
+ AlarmManager am = (AlarmManager)owner.context.getSystemService(Context.ALARM_SERVICE);
+ am.cancel(owner.alarmListener);
+ if (owner.thread != null) {
+ owner.handler = null;
+ owner.thread.quit();
+ owner.thread = null;
+ }
+
owner.stopHttpProxy();
final Tunnel tunnel = owner.currentTunnel;
if (tunnel != null) {
@@ -847,7 +917,7 @@ public final class GoBackend implements Backend {
@Override
public void onAvailable(Network network) {
Log.w(TAG, "VPN onAvailable: " + network);
- if (obtainDhcpLease) {
+ if (obtainDhcpLease && activeNetwork != null) {
Log.w(TAG, "Obtaindhcplease");
try {
Log.w(TAG, "Before Dhcp");
@@ -868,6 +938,20 @@ public final class GoBackend implements Backend {
}
@Override
+ public void onLosing(Network network, int maxMsToLive) {
+ Log.w(TAG, "onLosing: " + network + " maxMsToLive: " + maxMsToLive);
+ // TODO release DHCP?
+ }
+
+ @Override
+ public void onLost(Network network) {
+ Log.w(TAG, "onLost: " + network + " (" + activeNetwork + ")");
+ if (network.equals(activeNetwork)) {
+ activeNetwork = null;
+ }
+ }
+
+ @Override
public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
Log.w(TAG, "onLinkPropertiesChanged: " + network + " is default:" + (network.equals(activeNetwork)));
if (network.equals(activeNetwork) && currentConfig != null && currentTunnelHandle > -1) {
@@ -889,4 +973,13 @@ public final class GoBackend implements Backend {
}
}
}
+
+ private AlarmManager.OnAlarmListener alarmListener = new AlarmManager.OnAlarmListener() {
+ @Override
+ public void onAlarm() {
+ if (handler != null) {
+ handler.sendEmptyMessage(MSG_DHCP_EXPIRE);
+ }
+ }
+ };
}
diff --git a/tunnel/tools/libwg-go/dhcp.go b/tunnel/tools/libwg-go/dhcp.go
index b7919151..4af5b0cb 100644
--- a/tunnel/tools/libwg-go/dhcp.go
+++ b/tunnel/tools/libwg-go/dhcp.go
@@ -12,6 +12,7 @@ import (
"github.com/insomniacslk/dhcp/iana"
gen "golang.zx2c4.com/wireguard/android/gen"
+ "google.golang.org/protobuf/types/known/durationpb"
)
const (
@@ -131,13 +132,13 @@ func getAddressesFromReply(reply *dhcpv6.Message) []*gen.Lease{
ianaOpts := iana.Options.Get(dhcpv6.OptionIAAddr)
for _, opt := range ianaOpts {
- addr :=opt.(*dhcpv6.OptIAAddress).IPv6Addr
+ addrOpt := opt.(*dhcpv6.OptIAAddress)
lease := &gen.Lease{
Address: &gen.InetAddress{
- Address: addr,
+ Address: addrOpt.IPv6Addr,
},
- // PreferredLifetime: ,
- // ValidLifetime: ,
+ PreferredLifetime: durationpb.New(addrOpt.PreferredLifetime),
+ ValidLifetime: durationpb.New(addrOpt.ValidLifetime),
}
leases = append(leases, lease)
}