From 421bae85375dbef7fb64759eae22134d3c42a1fe Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Mon, 15 Mar 2021 20:22:58 +0000 Subject: WIP: implement dhcp in tunnel --- .../com/wireguard/android/backend/GoBackend.java | 104 +++++++++++++++++---- tunnel/tools/libwg-go/api-android.go | 51 ++++++++-- tunnel/tools/libwg-go/go.mod | 2 +- tunnel/tools/libwg-go/jni.c | 27 +++++- 4 files changed, 155 insertions(+), 29 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 edf6ac9e..f077189a 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java @@ -18,6 +18,7 @@ import com.wireguard.android.util.SharedLibraryLoader; import com.wireguard.config.Config; 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; @@ -25,6 +26,7 @@ import com.wireguard.util.NonNullForAll; import java.net.InetAddress; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; @@ -76,10 +78,14 @@ public final class GoBackend implements Backend, EventHandler { 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, EventHandler handler); + private static native int wgTurnOnDhcp(String ifName, String settings, EventHandler handler); + private static native String wgVersion(); /** @@ -205,14 +211,9 @@ public final class GoBackend implements Backend, EventHandler { return getState(tunnel); } - private void setStateInternal(final Tunnel tunnel, @Nullable final Config config, final State state) + private void setStateInternalFinalize(final Tunnel tunnel, 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); @@ -231,12 +232,6 @@ public final class GoBackend implements Backend, EventHandler { } service.setOwner(this); - if (currentTunnelHandle != -1) { - Log.w(TAG, "Tunnel already up"); - return; - } - - 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()) { @@ -268,9 +263,13 @@ public final class GoBackend implements Backend, EventHandler { 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()); @@ -304,7 +303,11 @@ public final class GoBackend implements Backend, EventHandler { if (tun == null) throw new BackendException(Reason.TUN_CREATION_ERROR); Log.d(TAG, "Go backend " + wgVersion()); - currentTunnelHandle = wgTurnOn(tunnel.getName(), tun.detachFd(), goConfig, this); + 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); @@ -314,6 +317,35 @@ public final class GoBackend implements Backend, EventHandler { service.protect(wgGetSocketV4(currentTunnelHandle)); service.protect(wgGetSocketV6(currentTunnelHandle)); + } + } + + 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"); @@ -330,7 +362,43 @@ public final class GoBackend implements Backend, EventHandler { } public void onEvent(String event) { - Log.d(TAG, "onEvent: " + 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); + } + } } /** diff --git a/tunnel/tools/libwg-go/api-android.go b/tunnel/tools/libwg-go/api-android.go index 2fb6e40f..cadbab12 100644 --- a/tunnel/tools/libwg-go/api-android.go +++ b/tunnel/tools/libwg-go/api-android.go @@ -32,6 +32,7 @@ import ( "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" @@ -63,12 +64,21 @@ type TunnelHandle struct { eventHandler unsafe.Pointer cancel context.CancelFunc master *ipvlan.IPVLANMaster + // tunEP *stack.LinkEndpoint + tunFd int } +var globalStack *stack.Stack var tunnelHandles map[int32]TunnelHandle 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() { @@ -200,12 +210,7 @@ func wgTurnOnDhcp(interfaceName string, settings string, eventHandler unsafe.Poi Errorf: AndroidLogger{level: C.ANDROID_LOG_ERROR, tag: tag}.Printf, } - opts := stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, - TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol}, - HandleLocal: true, - } - stack := stack.New(opts) + stack := globalStack // FIXME IP address ipStr := "fe80::101" @@ -308,14 +313,19 @@ func wgTurnOnDhcp(interfaceName string, settings string, eventHandler unsafe.Poi 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() + } } - // TODO move to wgSetTunFd - // tunEP := newTun(stack, 2, "tun0") - // master.SetIntEP(tunEP) + C.wgOnEvent(tunnelHandles[i].eventHandler, C.CString(msg)) }(ctx) logger.Verbosef("Before wgOnEvent %v", eventHandler) @@ -325,12 +335,35 @@ func wgTurnOnDhcp(interfaceName string, settings string, eventHandler unsafe.Poi 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) diff --git a/tunnel/tools/libwg-go/go.mod b/tunnel/tools/libwg-go/go.mod index 6fdb520c..98bbf52a 100644 --- a/tunnel/tools/libwg-go/go.mod +++ b/tunnel/tools/libwg-go/go.mod @@ -13,4 +13,4 @@ require ( golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect ) -replace golang.zx2c4.com/wireguard/tun/netstack => golang.m7n.se/wireguard/tun/netstack v0.0.0-20210315004706-824f10afd504 +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 de6a1e85..3897b2b7 100644 --- a/tunnel/tools/libwg-go/jni.c +++ b/tunnel/tools/libwg-go/jni.c @@ -14,6 +14,7 @@ struct go_string { const char *str; long n; }; 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); @@ -53,6 +54,25 @@ JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) } 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); @@ -62,7 +82,7 @@ JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOn(JNI int ret = wgTurnOnDhcp((struct go_string){ .str = ifname_str, .n = ifname_len - }, /* tun_fd,*/ (struct go_string){ + }, (struct go_string){ .str = settings_str, .n = settings_len }, event_handler); @@ -71,6 +91,11 @@ JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOn(JNI 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) { jobject event_handler = wgTurnOff(handle); -- cgit v1.2.3