summaryrefslogtreecommitdiffhomepage
path: root/tunnel/src
diff options
context:
space:
mode:
authorMikael Magnusson <mikma@users.sourceforge.net>2023-02-11 22:24:31 +0100
committerMikael Magnusson <mikma@users.sourceforge.net>2023-05-21 22:10:27 +0200
commit48de789902aae506b5c07f38b07fdb03186b36ab (patch)
treefe04a91dacf143dd538055eed9c9042d7ba3a66d /tunnel/src
parent9bdb538694fcd13acaf72c30d4254b595e5078f4 (diff)
tunnel: request DHCPv6 leases
Diffstat (limited to 'tunnel/src')
-rw-r--r--tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java230
-rw-r--r--tunnel/src/main/java/com/wireguard/util/Resolver.java2
-rw-r--r--tunnel/src/main/proto/libwg.proto19
3 files changed, 194 insertions, 57 deletions
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 ac1d03c4..c4c8f70a 100644
--- a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
+++ b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
@@ -28,9 +28,12 @@ import com.google.protobuf.Empty;
import com.wireguard.android.backend.BackendException.Reason;
import com.wireguard.android.backend.Tunnel.State;
+import com.wireguard.android.backend.gen.DhcpRequest;
+import com.wireguard.android.backend.gen.DhcpResponse;
import com.wireguard.android.backend.gen.GetConnectionOwnerUidResponse;
import com.wireguard.android.backend.gen.IpcSetRequest;
import com.wireguard.android.backend.gen.IpcSetResponse;
+import com.wireguard.android.backend.gen.Lease;
import com.wireguard.android.backend.gen.LibwgGrpc;
import com.wireguard.android.backend.gen.ReverseRequest;
import com.wireguard.android.backend.gen.ReverseResponse;
@@ -71,6 +74,7 @@ import java.net.URL;
import java.time.Instant;
import java.nio.ByteOrder;
import java.util.Collections;
+import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -103,7 +107,9 @@ public final class GoBackend implements Backend {
private ManagedChannel channel;
private ConnectivityManager connectivityManager;
private ConnectivityManager.NetworkCallback myNetworkCallback = new MyNetworkCallback();
+ private ConnectivityManager.NetworkCallback vpnNetworkCallback;
@Nullable private Network activeNetwork;
+ private boolean obtainDhcpLease = false;
/**
* Public constructor for GoBackend.
@@ -136,6 +142,8 @@ public final class GoBackend implements Backend {
private static native int wgGetSocketV6(int handle);
+ private static native void wgSetFd(int handle, int tunFd);
+
private static native void wgTurnOff(int handle);
private static native int wgTurnOn(String ifName, int tunFd, String settings);
@@ -330,6 +338,50 @@ public final class GoBackend implements Backend {
}
}
+ private void Dhcp(VpnService service) throws Exception{
+ obtainDhcpLease = false;
+
+ // Heuristics: Use first ULA address as client address
+ com.wireguard.android.backend.gen.InetAddress source = null;
+
+ for (final InetNetwork net : currentConfig.getInterface().getAddresses()) {
+ InetAddress addr = net.getAddress();
+ if (addr instanceof Inet6Address) {
+ if (Resolver.isULA((Inet6Address)addr)) {
+ source = com.wireguard.android.backend.gen.InetAddress.newBuilder().setAddress(ByteString.copyFrom(addr.getAddress())).build();
+ }
+ }
+ }
+
+ LibwgGrpc.LibwgBlockingStub stub = LibwgGrpc.newBlockingStub(channel);
+ DhcpRequest.Builder requestBuilder = DhcpRequest.newBuilder();
+ if (source != null) {
+ requestBuilder.setSource(source);
+ }
+ DhcpRequest request = requestBuilder.build();
+ DhcpResponse resp = stub.dhcp(request);
+ Log.i(TAG, "Dhcp: " + resp.getError().getMessage());
+
+ // Replace the vpn tunnel
+ final VpnService.Builder builder = getBuilder(currentTunnel.getName(), currentConfig, service, resp.getLeasesList());
+
+ Log.i(TAG, "Builder: " + builder);
+
+ try (final ParcelFileDescriptor tun = builder.establish()) {
+ if (tun == null)
+ throw new BackendException(Reason.TUN_CREATION_ERROR);
+ Log.d(TAG, "Go backend " + wgVersion());
+ // SetFd
+ wgSetFd(currentTunnelHandle, tun.detachFd());
+ }
+ if (currentTunnelHandle < 0)
+ throw new BackendException(Reason.GO_ACTIVATION_ERROR_CODE, currentTunnelHandle);
+
+ service.protect(wgGetSocketV4(currentTunnelHandle));
+ service.protect(wgGetSocketV6(currentTunnelHandle));
+ Log.i(TAG, "Dhcp done");
+ }
+
private int getConnectionOwnerUid(int protocol, InetSocketAddress local, InetSocketAddress remote) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
return connectivityManager.getConnectionOwnerUid(protocol, local, remote);
@@ -412,6 +464,94 @@ 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 List<Lease> leases) throws PackageManager.NameNotFoundException {
+ Log.i(TAG, "Builder 1");
+ final VpnService.Builder builder = service.getBuilder();
+ Log.i(TAG, "Builder 2");
+ builder.setSession(name);
+
+ Log.i(TAG, "Builder 3");
+ for (final String excludedApplication : config.getInterface().getExcludedApplications())
+ builder.addDisallowedApplication(excludedApplication);
+
+ Log.i(TAG, "Builder 4");
+ for (final String includedApplication : config.getInterface().getIncludedApplications())
+ builder.addAllowedApplication(includedApplication);
+
+ Log.i(TAG, "Builder 5");
+ if (leases != null) {
+ for (final Lease lease: leases) {
+ try {
+ InetAddress addr = InetAddress.getByAddress(lease.getAddress().getAddress().toByteArray());
+ Log.i(TAG, "Lease: " + addr);
+ builder.addAddress(addr, 128);
+ } catch (UnknownHostException ex) {
+ // Ignore
+ }
+ }
+ }
+
+ Log.i(TAG, "Builder 6");
+ for (final InetNetwork addr : config.getInterface().getAddresses())
+ builder.addAddress(addr.getAddress(), addr.getMask());
+
+ Log.i(TAG, "Builder 7");
+ for (final InetAddress addr : config.getInterface().getDnsServers())
+ builder.addDnsServer(addr.getHostAddress());
+
+ Log.i(TAG, "Builder 8");
+ for (final String dnsSearchDomain : config.getInterface().getDnsSearchDomains())
+ builder.addSearchDomain(dnsSearchDomain);
+
+ Log.i(TAG, "Builder 9");
+ boolean sawDefaultRoute = false;
+ for (final Peer peer : config.getPeers()) {
+ for (final InetNetwork addr : peer.getAllowedIps()) {
+ if (addr.getMask() == 0)
+ sawDefaultRoute = true;
+ builder.addRoute(addr.getAddress(), addr.getMask());
+ }
+ }
+
+ Log.i(TAG, "Builder 10");
+ // "Kill-switch" semantics
+ if (!(sawDefaultRoute && config.getPeers().size() == 1)) {
+ builder.allowFamily(OsConstants.AF_INET);
+ builder.allowFamily(OsConstants.AF_INET6);
+ }
+
+ Log.i(TAG, "Builder 11");
+ builder.setMtu(config.getInterface().getMtu().orElse(1280));
+
+ Log.i(TAG, "Builder 12");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
+ builder.setMetered(false);
+ Log.i(TAG, "Builder 13");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+ service.setUnderlyingNetworks(null);
+
+ Log.i(TAG, "Builder 14");
+ Optional<HttpProxy> proxy = config.getInterface().getHttpProxy();
+ if (proxy.isPresent()) {
+ ProxyInfo pi = proxy.get().getProxyInfo();
+ Uri pacFileUrl = pi.getPacFileUrl();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ if (pacFileUrl != null && pacFileUrl != Uri.EMPTY) {
+ int listenPort = startHttpProxy(pacFileUrl);
+ ProxyInfo localPi = ProxyInfo.buildDirectProxy("localhost", listenPort);
+ builder.setHttpProxy(localPi);
+ } else {
+ builder.setHttpProxy(pi);
+ }
+ }
+ }
+
+ Log.i(TAG, "Builder 15");
+ builder.setBlocking(true);
+ return builder;
+ }
+
private void setStateInternal(final Tunnel tunnel, @Nullable final Config config, final State state)
throws Exception {
Log.i(TAG, "Bringing tunnel " + tunnel.getName() + ' ' + state);
@@ -472,63 +612,8 @@ public final class GoBackend implements Backend {
final String goConfig = config.toWgUserspaceString(resolver);
// Create the vpn tunnel with android API
- final VpnService.Builder builder = service.getBuilder();
- builder.setSession(tunnel.getName());
-
- for (final String excludedApplication : config.getInterface().getExcludedApplications())
- builder.addDisallowedApplication(excludedApplication);
-
- for (final String includedApplication : config.getInterface().getIncludedApplications())
- builder.addAllowedApplication(includedApplication);
-
- for (final InetNetwork addr : config.getInterface().getAddresses())
- builder.addAddress(addr.getAddress(), addr.getMask());
-
- for (final InetAddress addr : config.getInterface().getDnsServers())
- builder.addDnsServer(addr.getHostAddress());
-
- for (final String dnsSearchDomain : config.getInterface().getDnsSearchDomains())
- builder.addSearchDomain(dnsSearchDomain);
-
- boolean sawDefaultRoute = false;
- for (final Peer peer : config.getPeers()) {
- for (final InetNetwork addr : peer.getAllowedIps()) {
- if (addr.getMask() == 0)
- sawDefaultRoute = true;
- builder.addRoute(addr.getAddress(), addr.getMask());
- }
- }
-
- // "Kill-switch" semantics
- if (!(sawDefaultRoute && config.getPeers().size() == 1)) {
- builder.allowFamily(OsConstants.AF_INET);
- builder.allowFamily(OsConstants.AF_INET6);
- }
-
- builder.setMtu(config.getInterface().getMtu().orElse(1280));
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
- builder.setMetered(false);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
- service.setUnderlyingNetworks(null);
-
- Optional<HttpProxy> proxy = config.getInterface().getHttpProxy();
- if (proxy.isPresent()) {
- ProxyInfo pi = proxy.get().getProxyInfo();
- Uri pacFileUrl = pi.getPacFileUrl();
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- if (pacFileUrl != null && pacFileUrl != Uri.EMPTY) {
- int listenPort = startHttpProxy(pacFileUrl);
- ProxyInfo localPi = ProxyInfo.buildDirectProxy("localhost", listenPort);
- builder.setHttpProxy(localPi);
- } else {
- builder.setHttpProxy(pi);
- }
- }
- }
+ final VpnService.Builder builder = getBuilder(tunnel.getName(), config, service, null);
- builder.setBlocking(true);
try (final ParcelFileDescriptor tun = builder.establish()) {
if (tun == null)
throw new BackendException(Reason.TUN_CREATION_ERROR);
@@ -544,8 +629,14 @@ public final class GoBackend implements Backend {
service.protect(wgGetSocketV4(currentTunnelHandle));
service.protect(wgGetSocketV6(currentTunnelHandle));
+ obtainDhcpLease = true;
+
NetworkRequest req = new NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN).build();
connectivityManager.requestNetwork(req, myNetworkCallback);
+
+ NetworkRequest vpnReq = new NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_VPN).removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN).build();
+ vpnNetworkCallback = new VpnNetworkCallback(service);
+ connectivityManager.requestNetwork(vpnReq, vpnNetworkCallback);
} else {
if (currentTunnelHandle == -1) {
Log.w(TAG, "Tunnel already down");
@@ -556,6 +647,9 @@ public final class GoBackend implements Backend {
currentTunnelHandle = -1;
currentConfig = null;
stopHttpProxy();
+ if (vpnNetworkCallback != null)
+ connectivityManager.unregisterNetworkCallback(vpnNetworkCallback);
+ vpnNetworkCallback = null;
connectivityManager.unregisterNetworkCallback(myNetworkCallback);
activeNetwork = null;
wgTurnOff(handleToClose);
@@ -627,6 +721,9 @@ public final class GoBackend implements Backend {
final Tunnel tunnel = owner.currentTunnel;
if (tunnel != null) {
if (owner.currentTunnelHandle != -1) {
+ if (owner.vpnNetworkCallback != null)
+ owner.connectivityManager.unregisterNetworkCallback(owner.vpnNetworkCallback);
+ owner.vpnNetworkCallback = null;
owner.connectivityManager.unregisterNetworkCallback(owner.myNetworkCallback);
owner.activeNetwork = null;
wgTurnOff(owner.currentTunnelHandle);
@@ -657,6 +754,27 @@ public final class GoBackend implements Backend {
}
}
+ private class VpnNetworkCallback extends ConnectivityManager.NetworkCallback {
+ private VpnService service;
+ public VpnNetworkCallback(VpnService service) {
+ this.service = service;
+ }
+ @Override
+ public void onAvailable(Network network) {
+ Log.w(TAG, "VPN onAvailable: " + network);
+ if (obtainDhcpLease) {
+ Log.w(TAG, "Obtaindhcplease");
+ try {
+ Log.w(TAG, "Before Dhcp");
+ Dhcp(service);
+ Log.w(TAG, "After Dhcp");
+ } catch (Exception ex) {
+ Log.e(TAG, "DHCP failed: " + ex);
+ }
+ }
+ }
+ }
+
private class MyNetworkCallback extends ConnectivityManager.NetworkCallback {
@Override
public void onAvailable(Network network) {
diff --git a/tunnel/src/main/java/com/wireguard/util/Resolver.java b/tunnel/src/main/java/com/wireguard/util/Resolver.java
index f401b584..301e03e0 100644
--- a/tunnel/src/main/java/com/wireguard/util/Resolver.java
+++ b/tunnel/src/main/java/com/wireguard/util/Resolver.java
@@ -36,7 +36,7 @@ public class Resolver {
}
}
- static boolean isULA(Inet6Address addr) {
+ public static boolean isULA(Inet6Address addr) {
byte[] raw = addr.getAddress();
return ((raw[0] & 0xfe) == 0xfc);
}
diff --git a/tunnel/src/main/proto/libwg.proto b/tunnel/src/main/proto/libwg.proto
index e633ea46..90c4f46a 100644
--- a/tunnel/src/main/proto/libwg.proto
+++ b/tunnel/src/main/proto/libwg.proto
@@ -1,5 +1,7 @@
syntax = "proto3";
+import "google/protobuf/duration.proto";
+
option java_multiple_files = true;
option java_package = 'com.wireguard.android.backend.gen';
option java_outer_classname = "LibwgProto";
@@ -15,6 +17,7 @@ service Libwg {
rpc StopHttpProxy(StopHttpProxyRequest) returns (StopHttpProxyResponse);
rpc Reverse(stream ReverseRequest) returns (stream ReverseResponse);
rpc IpcSet(IpcSetRequest) returns (IpcSetResponse);
+ rpc Dhcp(DhcpRequest) returns (DhcpResponse);
}
message TunnelHandle { int32 handle = 1; }
@@ -101,3 +104,19 @@ message IpcSetRequest {
message IpcSetResponse {
Error error = 1;
}
+
+message Lease {
+ InetAddress address = 1;
+ google.protobuf.Duration preferred_lifetime = 2;
+ google.protobuf.Duration valid_lifetime = 3;
+}
+
+message DhcpRequest {
+ InetAddress relay = 1;
+ InetAddress source = 2;
+}
+
+message DhcpResponse {
+ Error error = 1;
+ repeated Lease leases = 2;
+}