diff options
author | Mikael Magnusson <mikma@users.sourceforge.net> | 2021-03-13 21:58:21 +0000 |
---|---|---|
committer | Mikael Magnusson <mikma@users.sourceforge.net> | 2023-02-10 21:30:04 +0100 |
commit | 6141eafb86c9dc72d39784b86d2136b25ea269f6 (patch) | |
tree | b8ad8a13fdaa784ef74eec08ef9b35148c30be80 | |
parent | 8cd49f9a8d3e14568d7c0fda696fe62efb14d0e9 (diff) |
dhcp
WIP: implement wgOnEvent
WIP: debug
WIP: netstack: implement dhcp and ipvlan
WIP: implement dhcp in tunnel
WIP: add dhcp information to detail
12 files changed, 562 insertions, 30 deletions
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<InetNetwork> 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<InetNetwork> 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 @@ -58,6 +58,12 @@ public final class WgQuickBackend implements Backend { } @Override + public DhcpInfo getDhcpInfo(final Tunnel tunnel) { + final DhcpInfo info = new DhcpInfo(); + return info; + } + + @Override public Set<String> getRunningTunnelNames() { final List<String> output = new ArrayList<>(); // Don't throw an exception here or nothing will show up in the UI. diff --git a/tunnel/tools/libwg-go/api-android.go b/tunnel/tools/libwg-go/api-android.go index 0ab80be9..619dfccd 100644 --- a/tunnel/tools/libwg-go/api-android.go +++ b/tunnel/tools/libwg-go/api-android.go @@ -7,10 +7,13 @@ package main // #cgo LDFLAGS: -llog // #include <android/log.h> +// extern void wgOnEvent(void *eventHandler, const char *event); import "C" import ( + "context" "fmt" + "log" "math" "net" "os" @@ -25,6 +28,16 @@ import ( "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/ipc" "golang.zx2c4.com/wireguard/tun" + "golang.zx2c4.com/wireguard/tun/netstack" + "golang.zx2c4.com/wireguard/tun/netstack/ipvlan" + + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/link/fdbased" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" + "gvisor.dev/gvisor/pkg/tcpip/stack" + "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" + "gvisor.dev/gvisor/pkg/tcpip/transport/udp" ) type AndroidLogger struct { @@ -49,8 +62,14 @@ type TunnelHandle struct { device *device.Device uapi net.Listener logger *device.Logger + eventHandler unsafe.Pointer + cancel context.CancelFunc + master *ipvlan.IPVLANMaster + // tunEP *stack.LinkEndpoint + tunFd int } +var globalStack *stack.Stack var tunnelHandles map[int32]TunnelHandle func GetTunnel(handle int32) (tunnelHandle TunnelHandle, ok bool) { @@ -60,6 +79,12 @@ func GetTunnel(handle int32) (tunnelHandle TunnelHandle, ok bool) { func init() { tunnelHandles = make(map[int32]TunnelHandle) + opts := stack.Options{ + NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, + TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol}, + HandleLocal: true, + } + globalStack = stack.New(opts) signals := make(chan os.Signal) signal.Notify(signals, unix.SIGUSR2) go func() { @@ -79,7 +104,7 @@ func init() { } //export wgTurnOn -func wgTurnOn(interfaceName string, tunFd int32, settings string) int32 { +func wgTurnOn(interfaceName string, tunFd int32, settings string, eventHandler unsafe.Pointer) int32 { tag := cstring("WireGuard/GoBackend/" + interfaceName) logger := &device.Logger{ Verbosef: AndroidLogger{level: C.ANDROID_LOG_DEBUG, tag: tag}.Printf, @@ -148,21 +173,211 @@ func wgTurnOn(interfaceName string, tunFd int32, settings string) int32 { device.Close() return -1 } - tunnelHandles[i] = TunnelHandle{device: device, uapi: uapi} + tunnelHandles[i] = TunnelHandle{device: device, uapi: uapi, eventHandler: eventHandler} + logger.Verbosef("Before wgOnEvent %v", eventHandler) + C.wgOnEvent(tunnelHandles[i].eventHandler, C.CString("FOOBAR")) + C.wgOnEvent(tunnelHandles[i].eventHandler, C.CString("Called wgTurnOn")) + logger.Verbosef("After wgOnEvent") return i } -//export wgTurnOff -func wgTurnOff(tunnelHandle int32) { +func createNetTUNWithStack(stack *stack.Stack, nicID tcpip.NICID, localAddresses []net.IP, dnsServers []net.IP, mtu int) (tun.Device, *netstack.Net, *ipvlan.IPVLANMaster, error) { + dev, ep, err := netstack.NewNetTUN(stack, mtu) + if err != nil { + return nil, nil, nil, err + } + + master := ipvlan.NewIPVLANMaster(ep) + + addrs := make([]tcpip.Address, 0, len(localAddresses)) + for _, addr := range localAddresses { + addrs = append(addrs, tcpip.Address(addr)) + } + ipvlanEP := master.NewIPVLAN(addrs) + + tcpipErr := stack.CreateNIC(nicID, ipvlanEP) + if tcpipErr != nil { + return nil, nil, nil, fmt.Errorf("CreateNIC: %v", tcpipErr) + } + + tnet, err := netstack.NewNetAdapter(stack, nicID, localAddresses, dnsServers) + if err != nil { + return nil, nil, nil, err + } + + return dev, tnet, master, nil +} + +//export wgTurnOnDhcp +func wgTurnOnDhcp(interfaceName string, settings string, eventHandler unsafe.Pointer) int32 { + tag := cstring("WireGuard/GoBackend/" + interfaceName) + logger := &device.Logger{ + Verbosef: AndroidLogger{level: C.ANDROID_LOG_DEBUG, tag: tag}.Printf, + Errorf: AndroidLogger{level: C.ANDROID_LOG_ERROR, tag: tag}.Printf, + } + + stack := globalStack + + // FIXME IP address + ipStr := "fe80::101" + // FIXME name + name := "wg0" + var tundev tun.Device + var tnet *netstack.Net + tundev, tnet, master, err := createNetTUNWithStack( + stack, + 1, + []net.IP{net.ParseIP(ipStr)}, + // []net.IP{net.ParseIP("fe80::1")}, + []net.IP{net.ParseIP("fe80::1")}, + 1420) + if err != nil { + log.Panic(err) + } + + logger.Verbosef("Attaching to netstack interface") + device := device.NewDevice(tundev, conn.NewStdNetBind(), logger) + + err = device.IpcSet(settings) + if err != nil { + logger.Errorf("IpcSet: %v", err) + return -1 + } + device.DisableSomeRoamingForBrokenMobileSemantics() + + var uapi net.Listener + + uapiFile, err := ipc.UAPIOpen(name) + if err != nil { + logger.Errorf("UAPIOpen: %v", err) + } else { + uapi, err = ipc.UAPIListen(name, uapiFile) + if err != nil { + uapiFile.Close() + logger.Errorf("UAPIListen: %v", err) + } else { + go func() { + for { + conn, err := uapi.Accept() + if err != nil { + return + } + go device.IpcHandle(conn) + } + }() + } + } + + err = device.Up() + if err != nil { + logger.Errorf("Unable to bring up device: %v", err) + uapiFile.Close() + device.Close() + return -1 + } + logger.Verbosef("Device started") + + var i int32 + for i = 0; i < math.MaxInt32; i++ { + if _, exists := tunnelHandles[i]; !exists { + break + } + } + if i == math.MaxInt32 { + logger.Errorf("Unable to find empty handle") + uapiFile.Close() + device.Close() + return -1 + } + ctx, cancel := context.WithCancel(context.Background()) + + tunnelHandles[i] = TunnelHandle{device: device, uapi: uapi, eventHandler: eventHandler, cancel: cancel, master: master} + + go func(ctx context.Context) { + logger.Verbosef("Start dhcp client") + src, err := net.ResolveUDPAddr("udp6", fmt.Sprintf("[%s%%1]:546", ipStr)) + if err != nil { + logger.Errorf("ResolveUDPAddr: %v", err) + return + } + + var dst *net.UDPAddr + dst = nil + + conn, err := tnet.DialUDP(src, dst) + if err != nil { + logger.Errorf("DialUDP: %v", err) + return + } + + logger.Verbosef("Conn: %v %v %v", src, dst, conn) + hwAddr := []byte(" 101") + addrs, err := netstack.RunDhcp(ctx, conn, hwAddr) + + if err != nil { + logger.Errorf("DHCP: %v", err) + return + } + + msg := "" + + logger.Verbosef("DHCP finished") + for _, addr := range addrs { + logger.Verbosef("Address: %v", addr) + if msg == "" { + msg = "event=1\naddress=" + addr.String() + } else { + msg += "," + addr.String() + } + } + + C.wgOnEvent(tunnelHandles[i].eventHandler, C.CString(msg)) + }(ctx) + + logger.Verbosef("Before wgOnEvent %v", eventHandler) + C.wgOnEvent(tunnelHandles[i].eventHandler, C.CString("FOOBAR")) + C.wgOnEvent(tunnelHandles[i].eventHandler, C.CString("Called wgTurnOn")) + logger.Verbosef("After wgOnEvent") + return i +} + +//export wgSetFd +func wgSetFd(tunnelHandle int32, tunFd int32) { handle, ok := tunnelHandles[tunnelHandle] if !ok { return } + + // FIXME MTU + var mtu uint32 = 1280 + tunEP, err := fdbased.New(&fdbased.Options{FDs: []int{int(tunFd)}, MTU: mtu}) + if err != nil { + log.Fatal(err) + } + handle.tunFd = int(tunFd) + handle.master.SetIntEP(tunEP) +} + +//export wgTurnOff +func wgTurnOff(tunnelHandle int32) unsafe.Pointer { + handle, ok := tunnelHandles[tunnelHandle] + if !ok { + return nil + } + if handle.master != nil { + globalStack.RemoveNIC(1) + handle.master.SetIntEP(nil) + unix.Close(handle.tunFd) + } + + eventHandler := handle.eventHandler + C.wgOnEvent(handle.eventHandler, C.CString("Called wgTurnOff")) delete(tunnelHandles, tunnelHandle) if handle.uapi != nil { handle.uapi.Close() } handle.device.Close() + return eventHandler } //export wgGetSocketV4 @@ -171,6 +386,7 @@ func wgGetSocketV4(tunnelHandle int32) int32 { if !ok { return -1 } + C.wgOnEvent(handle.eventHandler, C.CString("Called wgGetSocketV4")) bind, _ := handle.device.Bind().(conn.PeekLookAtSocketFd) if bind == nil { return -1 @@ -188,6 +404,7 @@ func wgGetSocketV6(tunnelHandle int32) int32 { if !ok { return -1 } + C.wgOnEvent(handle.eventHandler, C.CString("Called wgGetSocketV6")) bind, _ := handle.device.Bind().(conn.PeekLookAtSocketFd) if bind == nil { return -1 diff --git a/tunnel/tools/libwg-go/go.mod b/tunnel/tools/libwg-go/go.mod index 49fe3780..d9ee9e25 100644 --- a/tunnel/tools/libwg-go/go.mod +++ b/tunnel/tools/libwg-go/go.mod @@ -19,3 +19,5 @@ require ( golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect ) + +replace golang.zx2c4.com/wireguard/tun/netstack => golang.m7n.se/wireguard/tun/netstack v0.0.0-20210315192121-e7c9dcf39d3f diff --git a/tunnel/tools/libwg-go/jni.c b/tunnel/tools/libwg-go/jni.c index 121729a7..5dd20ed2 100644 --- a/tunnel/tools/libwg-go/jni.c +++ b/tunnel/tools/libwg-go/jni.c @@ -3,40 +3,105 @@ * Copyright © 2017-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */ +#include <assert.h> #include <jni.h> #include <stdlib.h> #include <string.h> +#include <android/log.h> + +#define TAG "WireGuard/GoBackend" struct go_string { const char *str; long n; }; -extern int wgTurnOn(struct go_string ifname, int tun_fd, struct go_string settings); -extern void wgTurnOff(int handle); +extern int wgTurnOn(struct go_string ifname, int tun_fd, struct go_string settings, void *eventHandler); +extern int wgTurnOnDhcp(struct go_string ifname, struct go_string settings, void *eventHandler); +extern void wgSetFd(int handle, int tun_fd); +extern void *wgTurnOff(int handle); extern int wgGetSocketV4(int handle); extern int wgGetSocketV6(int handle); extern char *wgGetConfig(int handle); extern char *wgVersion(); extern int wgStartGrpc(); +extern void wgOnEvent(void *eventHandler, const char *event); + +static JavaVM *gVm; +static jclass gEventHandlerClass; +static jmethodID gOnEventMethod; -JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOn(JNIEnv *env, jclass c, jstring ifname, jint tun_fd, jstring settings) +JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) +{ + JNIEnv* env; + char buf[128]; + + __android_log_write(ANDROID_LOG_ERROR, TAG, "JNI_OnLoad"); + + gVm = vm; + if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) { + __android_log_write(ANDROID_LOG_ERROR, TAG, "GetEnv error"); + return JNI_ERR; + } + + sprintf(buf, "GetEnv FindClass %p", (*env)->FindClass); + __android_log_write(ANDROID_LOG_ERROR, TAG, buf); + gEventHandlerClass = (*env)->FindClass(env, "com/wireguard/android/backend/EventHandler"); + sprintf(buf, "gEventHandlerClass %p", gEventHandlerClass); + __android_log_write(ANDROID_LOG_ERROR, TAG, buf); + + gOnEventMethod = (*env)->GetMethodID(env, gEventHandlerClass, "onEvent", "(Ljava/lang/String;)V"); + sprintf(buf, "gOnEventMethod %p", gOnEventMethod); + __android_log_write(ANDROID_LOG_ERROR, TAG, buf); + //assert(gOnEventMethod); + + return JNI_VERSION_1_6; +} + +JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOn(JNIEnv *env, jclass c, jstring ifname, jint tun_fd, jstring settings, jobject eventHandler) { const char *ifname_str = (*env)->GetStringUTFChars(env, ifname, 0); size_t ifname_len = (*env)->GetStringUTFLength(env, ifname); const char *settings_str = (*env)->GetStringUTFChars(env, settings, 0); size_t settings_len = (*env)->GetStringUTFLength(env, settings); + jobject event_handler = (*env)->NewGlobalRef(env, eventHandler); int ret = wgTurnOn((struct go_string){ .str = ifname_str, .n = ifname_len }, tun_fd, (struct go_string){ .str = settings_str, .n = settings_len - }); + }, event_handler); (*env)->ReleaseStringUTFChars(env, ifname, ifname_str); (*env)->ReleaseStringUTFChars(env, settings, settings_str); return ret; } +JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOnDhcp(JNIEnv *env, jclass c, jstring ifname, jstring settings, jobject eventHandler) +{ + const char *ifname_str = (*env)->GetStringUTFChars(env, ifname, 0); + size_t ifname_len = (*env)->GetStringUTFLength(env, ifname); + const char *settings_str = (*env)->GetStringUTFChars(env, settings, 0); + size_t settings_len = (*env)->GetStringUTFLength(env, settings); + jobject event_handler = (*env)->NewGlobalRef(env, eventHandler); + int ret = wgTurnOnDhcp((struct go_string){ + .str = ifname_str, + .n = ifname_len + }, (struct go_string){ + .str = settings_str, + .n = settings_len + }, event_handler); + (*env)->ReleaseStringUTFChars(env, ifname, ifname_str); + (*env)->ReleaseStringUTFChars(env, settings, settings_str); + return ret; +} + +JNIEXPORT void JNICALL Java_com_wireguard_android_backend_GoBackend_wgSetFd(JNIEnv *env, jclass c, jint handle, jint tun_fd) +{ + wgSetFd(handle, tun_fd); +} + JNIEXPORT void JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOff(JNIEnv *env, jclass c, jint handle) { - wgTurnOff(handle); + jobject event_handler = wgTurnOff(handle); + if (event_handler) + (*env)->DeleteGlobalRef(env, event_handler); } JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgGetSocketV4(JNIEnv *env, jclass c, jint handle) @@ -82,3 +147,29 @@ JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgStartGrpc( (*env)->ReleaseStringUTFChars(env, sockname, sockname_str); return res; } + +void wgOnEvent(void *eventHandler, const char *event) +{ + JavaVMAttachArgs attachArgs = { JNI_VERSION_1_6, NULL, NULL }; + jobject jEventHandler = eventHandler; + JNIEnv *env; + char buf[128]; + + __android_log_write(ANDROID_LOG_ERROR, TAG, "wgOnEvent enter 2"); + jint rs = (*gVm)->AttachCurrentThread(gVm, &env, &attachArgs); + + sprintf(buf, "AttachCurrentThread %i %p", rs, env); + __android_log_write(ANDROID_LOG_ERROR, TAG, buf); + assert (rs == JNI_OK); + jstring jevent = (*env)->NewStringUTF(env, event); + sprintf(buf, "NewStringUTF %p", jevent); + __android_log_write(ANDROID_LOG_ERROR, TAG, buf); + //free(event); + sprintf(buf, "CallVoidMethod %p %p %p %p", env, jEventHandler, gOnEventMethod, jevent); + __android_log_write(ANDROID_LOG_ERROR, TAG, buf); + (*env)->CallVoidMethod(env, jEventHandler, gOnEventMethod, jevent); + /* sprintf(buf, "CallVoidMethod"); */ + /* __android_log_write(ANDROID_LOG_ERROR, TAG, buf); */ + // TODO free jevent? + __android_log_write(ANDROID_LOG_ERROR, TAG, "wgOnEvent end"); +} diff --git a/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt b/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt index fad1b5d4..f2cf9040 100644 --- a/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt +++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt @@ -104,6 +104,10 @@ class TunnelDetailFragment : BaseFragment() { lastState = state var now = LocalDateTime.now(ZoneId.of("UTC")) try { + binding.dhcpInfoText.text = tunnel.getDhcpInfo().get() + binding.dhcpInfoLabel.visibility = View.VISIBLE + binding.dhcpInfoText.visibility = View.VISIBLE + val statistics = tunnel.getStatisticsAsync() for (i in 0 until binding.peersLayout.childCount) { val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding.peersLayout.getChildAt(i)) diff --git a/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt b/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt index 252e8759..edf8feaf 100644 --- a/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt +++ b/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt @@ -8,6 +8,7 @@ import android.util.Log import androidx.databinding.BaseObservable import androidx.databinding.Bindable import com.wireguard.android.BR +import com.wireguard.android.backend.DhcpInfo import com.wireguard.android.backend.Statistics import com.wireguard.android.backend.Tunnel import com.wireguard.android.databinding.Keyed @@ -136,6 +137,35 @@ class ObservableTunnel internal constructor( return statistics } + @get:Bindable + var dhcpInfo: DhcpInfo? = null + get() { + if (field == null || field?.isStale != false) + applicationScope.launch { + try { + manager.getTunnelDhcpInfo(this@ObservableTunnel) + } catch (e: Throwable) { + Log.e(TAG, Log.getStackTraceString(e)) + } + } + return field + } + private set + + suspend fun getDhcpInfo(): DhcpInfo = withContext(Dispatchers.Main.immediate) { + dhcpInfo.let { + if (it == null || it.isStale) + manager.getTunnelDhcpInfo(this@ObservableTunnel) + else + it + } + } + + fun onDhcpInfoChanged(dhcpInfo: DhcpInfo?): DhcpInfo? { + this.dhcpInfo = dhcpInfo + notifyPropertyChanged(BR.dhcpInfo) + return dhcpInfo + } suspend fun deleteAsync() = manager.delete(this) diff --git a/ui/src/main/java/com/wireguard/android/model/TunnelManager.kt b/ui/src/main/java/com/wireguard/android/model/TunnelManager.kt index ec796164..ac39ac4a 100644 --- a/ui/src/main/java/com/wireguard/android/model/TunnelManager.kt +++ b/ui/src/main/java/com/wireguard/android/model/TunnelManager.kt @@ -17,6 +17,7 @@ import com.wireguard.android.Application.Companion.getBackend import com.wireguard.android.Application.Companion.getTunnelManager import com.wireguard.android.BR import com.wireguard.android.R +import com.wireguard.android.backend.DhcpInfo import com.wireguard.android.backend.Statistics import com.wireguard.android.backend.Tunnel import com.wireguard.android.configStore.ConfigStore @@ -240,6 +241,10 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() { } } + suspend fun getTunnelDhcpInfo(tunnel: ObservableTunnel): DhcpInfo = withContext(Dispatchers.Main.immediate) { + tunnel.onDhcpInfoChanged(withContext(Dispatchers.IO) { getBackend().getDhcpInfo(tunnel) })!! + } + suspend fun getTunnelState(tunnel: ObservableTunnel): Tunnel.State = withContext(Dispatchers.Main.immediate) { tunnel.onStateChanged(withContext(Dispatchers.IO) { getBackend().getState(tunnel) }) } diff --git a/ui/src/main/res/layout/tunnel_detail_fragment.xml b/ui/src/main/res/layout/tunnel_detail_fragment.xml index aef71e18..c004fc5a 100644 --- a/ui/src/main/res/layout/tunnel_detail_fragment.xml +++ b/ui/src/main/res/layout/tunnel_detail_fragment.xml @@ -322,14 +322,42 @@ android:layout_height="wrap_content" android:contentDescription="@string/applications" android:nextFocusUp="@id/mtu_text" - android:nextFocusDown="@id/peers_layout" - android:nextFocusForward="@id/peers_layout" + android:nextFocusDown="@id/dhcp_info_text" + android:nextFocusForward="@id/dhcp_info_text" android:onClick="@{ClipboardUtils::copyTextView}" android:text="@{config.interface.includedApplications.isEmpty() ? @plurals/n_excluded_applications(config.interface.excludedApplications.size(), config.interface.excludedApplications.size()) : @plurals/n_included_applications(config.interface.includedApplications.size(), config.interface.includedApplications.size())}" android:visibility="@{config.interface.includedApplications.isEmpty() && config.interface.excludedApplications.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}" app:layout_constraintTop_toBottomOf="@+id/applications_label" app:layout_constraintStart_toStartOf="parent" tools:text="8 excluded" /> + + <TextView + android:id="@+id/dhcp_info_label" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:labelFor="@+id/dhcp_info_text" + android:text="@string/dhcp_info" + android:visibility="gone" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/applications_text" /> + + <TextView + android:id="@+id/dhcp_info_text" + style="@style/DetailText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:contentDescription="@string/dhcp_info" + android:nextFocusUp="@id/applications_text" + android:nextFocusDown="@id/peers_layout" + android:nextFocusForward="@id/peers_layout" + android:onClick="@{ClipboardUtils::copyTextView}" + android:visibility="gone" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/dhcp_info_label" + tools:text="Addresses: 192.0.2.123/24, 2001:db8::123/128" + tools:visibility="visible" /> + </androidx.constraintlayout.widget.ConstraintLayout> </com.google.android.material.card.MaterialCardView> diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index 3b32ab79..4da4e8fe 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -248,4 +248,6 @@ <string name="biometric_prompt_private_key_title">Authenticate to view private key</string> <string name="biometric_auth_error">Authentication failure</string> <string name="biometric_auth_error_reason">Authentication failure: %s</string> + <string name="dhcp_info">DHCP information</string> + <string name="dhcp_info_text">Addresses: %s\nIPv6 prefixes: %s\nDNS servers: %s</string> </resources> |