From 6141eafb86c9dc72d39784b86d2136b25ea269f6 Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Sat, 13 Mar 2021 21:58:21 +0000 Subject: dhcp WIP: implement wgOnEvent WIP: debug WIP: netstack: implement dhcp and ipvlan WIP: implement dhcp in tunnel WIP: add dhcp information to detail --- .../com/wireguard/android/backend/Backend.java | 2 + .../com/wireguard/android/backend/DhcpInfo.java | 57 ++++++++++ .../com/wireguard/android/backend/GoBackend.java | 126 +++++++++++++++++---- .../wireguard/android/backend/WgQuickBackend.java | 6 + 4 files changed, 172 insertions(+), 19 deletions(-) create mode 100644 tunnel/src/main/java/com/wireguard/android/backend/DhcpInfo.java (limited to 'tunnel/src/main/java/com/wireguard') diff --git a/tunnel/src/main/java/com/wireguard/android/backend/Backend.java b/tunnel/src/main/java/com/wireguard/android/backend/Backend.java index 5aaad826..1d9be593 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/Backend.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/Backend.java @@ -18,6 +18,8 @@ import androidx.annotation.Nullable; @NonNullForAll public interface Backend { + DhcpInfo getDhcpInfo(Tunnel tunnel) throws Exception; + /** * Enumerate names of currently-running tunnels. * diff --git a/tunnel/src/main/java/com/wireguard/android/backend/DhcpInfo.java b/tunnel/src/main/java/com/wireguard/android/backend/DhcpInfo.java new file mode 100644 index 00000000..35a7dd43 --- /dev/null +++ b/tunnel/src/main/java/com/wireguard/android/backend/DhcpInfo.java @@ -0,0 +1,57 @@ +/* + * Copyright © 2020 WireGuard LLC. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.wireguard.android.backend; + +import android.os.SystemClock; +import android.util.Pair; + +import com.wireguard.crypto.Key; +import com.wireguard.util.NonNullForAll; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class representing DHCP info for a {@link Tunnel} instance. + */ +@NonNullForAll +public class DhcpInfo { + private String info; + private long lastTouched = SystemClock.elapsedRealtime(); + + DhcpInfo() { + } + + /** + * Add a peer and its current data usage to the internal map. + * + * @param key A WireGuard public key bound to a particular peer + * @param rx The received traffic for the {@link com.wireguard.config.Peer} referenced by + * the provided {@link Key}. This value is in bytes + * @param tx The transmitted traffic for the {@link com.wireguard.config.Peer} referenced by + * the provided {@link Key}. This value is in bytes. + */ + void set(final String info) { + this.info = info; + lastTouched = SystemClock.elapsedRealtime(); + } + + /** + * Check if the statistics are stale, indicating the need for the {@link Backend} to update them. + * + * @return boolean indicating if the current statistics instance has stale values. + */ + public boolean isStale() { + return SystemClock.elapsedRealtime() - lastTouched > 900; + } + + /** + */ + public String get() { + return info; + } + +} 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 e8148c04..4e0a9f94 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java @@ -46,6 +46,7 @@ import com.wireguard.config.Config; import com.wireguard.config.HttpProxy; import com.wireguard.config.InetEndpoint; import com.wireguard.config.InetNetwork; +import com.wireguard.config.ParseException; import com.wireguard.config.Peer; import com.wireguard.crypto.Key; import com.wireguard.crypto.KeyFormatException; @@ -71,6 +72,7 @@ import java.net.UnknownHostException; import java.net.URL; import java.nio.ByteOrder; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.Optional; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -91,7 +93,7 @@ import androidx.collection.ArraySet; * WireGuard tunnels. */ @NonNullForAll -public final class GoBackend implements Backend { +public final class GoBackend implements Backend, EventHandler { private static final int DNS_RESOLUTION_RETRIES = 10; private static final String TAG = "WireGuard/GoBackend"; @Nullable private static AlwaysOnCallback alwaysOnCallback; @@ -140,14 +142,29 @@ 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); + private static native int wgTurnOn(String ifName, int tunFd, String settings, EventHandler handler); + + private static native int wgTurnOnDhcp(String ifName, String settings, EventHandler handler); private static native String wgVersion(); private static native int wgStartGrpc(String sockName); + @Override + public DhcpInfo getDhcpInfo(final Tunnel tunnel) { + final DhcpInfo info = new DhcpInfo(); + if (tunnel != currentTunnel) { + return info; + } + // TODO update info + info.set("FIXME"); + return info; + } + /** * Method to get the names of running tunnels. * @@ -426,14 +443,9 @@ public final class GoBackend implements Backend { Log.i(TAG, "Exit streamReverse"); } - private void setStateInternal(final Tunnel tunnel, @Nullable final Config config, final State state) + private void setStateInternalFinalize(final Tunnel tunnel, @Nullable final Config config, Set addresses) throws Exception { - Log.i(TAG, "Bringing tunnel " + tunnel.getName() + ' ' + state); - - if (state == State.UP) { - if (config == null) - throw new BackendException(Reason.TUNNEL_MISSING_CONFIG); - + if (true) { // Add scope to avoid changing indentation level if (VpnService.prepare(context) != null) throw new BackendException(Reason.VPN_NOT_AUTHORIZED); @@ -452,18 +464,13 @@ public final class GoBackend implements Backend { } service.setOwner(this); - if (currentTunnelHandle != -1) { - Log.w(TAG, "Tunnel already up"); - return; - } - - activeNetwork = connectivityManager.getActiveNetwork(); if (!connectivityManager.getNetworkCapabilities(activeNetwork).hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)) { Log.w(TAG, "VPN network is active, null activeNetwork"); activeNetwork = null; } final Resolver resolver = new Resolver(activeNetwork, connectivityManager.getLinkProperties(activeNetwork)); + dnsRetry: for (int i = 0; i < DNS_RESOLUTION_RETRIES; ++i) { // Pre-resolve IPs so they're cached when building the userspace string for (final Peer peer : config.getPeers()) { @@ -495,9 +502,13 @@ public final class GoBackend implements Backend { for (final String includedApplication : config.getInterface().getIncludedApplications()) builder.addAllowedApplication(includedApplication); - for (final InetNetwork addr : config.getInterface().getAddresses()) - builder.addAddress(addr.getAddress(), addr.getMask()); - + if (addresses.size() > 0) { + for (final InetNetwork addr : addresses) + builder.addAddress(addr.getAddress(), addr.getMask()); + } else { + for (final InetNetwork addr : config.getInterface().getAddresses()) + builder.addAddress(addr.getAddress(), addr.getMask()); + } for (final InetAddress addr : config.getInterface().getDnsServers()) builder.addDnsServer(addr.getHostAddress()); @@ -547,7 +558,11 @@ public final class GoBackend implements Backend { if (tun == null) throw new BackendException(Reason.TUN_CREATION_ERROR); Log.d(TAG, "Go backend " + wgVersion()); - currentTunnelHandle = wgTurnOn(tunnel.getName(), tun.detachFd(), goConfig); + if (currentTunnelHandle < 0) { + currentTunnelHandle = wgTurnOn(tunnel.getName(), tun.detachFd(), goConfig, this); + } else { + wgSetFd(currentTunnelHandle, tun.detachFd()); + } } if (currentTunnelHandle < 0) throw new BackendException(Reason.GO_ACTIVATION_ERROR_CODE, currentTunnelHandle); @@ -560,6 +575,35 @@ public final class GoBackend implements Backend { NetworkRequest req = new NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN).build(); connectivityManager.requestNetwork(req, myNetworkCallback); + } + } + + private void setStateInternal(final Tunnel tunnel, @Nullable final Config config, final State state) + throws Exception { + Log.i(TAG, "Bringing tunnel " + tunnel.getName() + ' ' + state); + + if (state == State.UP) { + if (config == null) + throw new BackendException(Reason.TUNNEL_MISSING_CONFIG); + + if (currentTunnelHandle != -1) { + Log.w(TAG, "Tunnel already up"); + return; + } + + if (false) { + setStateInternalFinalize(tunnel, config, null); + } else { + // Build config + final String goConfig = config.toWgUserspaceString(); + + currentTunnelHandle = wgTurnOnDhcp(tunnel.getName(), goConfig, this); + if (currentTunnelHandle < 0) + throw new BackendException(Reason.GO_ACTIVATION_ERROR_CODE, currentTunnelHandle); + + currentTunnel = tunnel; + currentConfig = config; + } } else { if (currentTunnelHandle == -1) { Log.w(TAG, "Tunnel already down"); @@ -578,6 +622,46 @@ public final class GoBackend implements Backend { tunnel.onStateChange(state); } + public void onEvent(String event) { + boolean isEvent = false; + Set addresses = new LinkedHashSet<>(); + + Log.d(TAG, "onEvent: " + event); + for (final String line : event.split("\\n")) { + final String[] params = line.split("=", 2); + if (params.length != 2) + break; + + final String key = params[0]; + final String value = params[1]; + + if ("event".equals(key)) { + isEvent = true; + } else if (!isEvent) { + break; + } + + if ("address".equals(key)) { + for (final String strAddr : value.split(",")) { + try { + InetNetwork addr = InetNetwork.parse(strAddr); + if (addr.getMask() < 33 || addr.getMask() == 128) + addresses.add(addr); + } catch(final ParseException ignored) { + } + } + } + } + + if (addresses.size() > 0) { + try { + setStateInternalFinalize(currentTunnel, currentConfig, addresses); + } catch (final Exception e) { + Log.d(TAG, "setStateInternalFinalize", e); + } + } + } + /** * Callback for {@link GoBackend} that is invoked when {@link VpnService} is started by the * system's Always-On VPN mode. @@ -691,3 +775,7 @@ public final class GoBackend implements Backend { } } } + +interface EventHandler { + public void onEvent(String event); +} diff --git a/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java b/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java index d0dc6a46..06a4db86 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java @@ -57,6 +57,12 @@ public final class WgQuickBackend implements Backend { return new File("/sys/module/wireguard").exists(); } + @Override + public DhcpInfo getDhcpInfo(final Tunnel tunnel) { + final DhcpInfo info = new DhcpInfo(); + return info; + } + @Override public Set getRunningTunnelNames() { final List output = new ArrayList<>(); -- cgit v1.2.3