diff options
-rw-r--r-- | tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java | 214 | ||||
-rw-r--r-- | tunnel/src/main/proto/libwg.proto | 13 | ||||
-rw-r--r-- | tunnel/tools/libwg-go/Makefile | 2 | ||||
-rw-r--r-- | tunnel/tools/libwg-go/api-android.go | 22 | ||||
-rw-r--r-- | tunnel/tools/libwg-go/dhcp.go | 338 | ||||
-rw-r--r-- | tunnel/tools/libwg-go/go.mod | 3 | ||||
-rw-r--r-- | tunnel/tools/libwg-go/jni.c | 6 | ||||
-rw-r--r-- | tunnel/tools/libwg-go/service.go | 17 |
8 files changed, 361 insertions, 254 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 e8148c04..375de1f2 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.UnknownHostException; import java.net.URL; 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. @@ -140,6 +146,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); @@ -344,6 +352,34 @@ public final class GoBackend implements Backend { } } + private void Dhcp(VpnService service) throws Exception{ + obtainDhcpLease = false; + + LibwgGrpc.LibwgBlockingStub stub = LibwgGrpc.newBlockingStub(channel); + DhcpRequest request = DhcpRequest.newBuilder().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); @@ -426,6 +462,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); @@ -486,63 +610,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); @@ -558,8 +627,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"); @@ -570,6 +645,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); @@ -638,6 +716,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); @@ -668,6 +749,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/proto/libwg.proto b/tunnel/src/main/proto/libwg.proto index 591e897f..ab6b2659 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"; @@ -103,9 +105,16 @@ message IpcSetResponse { Error error = 1; } +message Lease { + InetAddress address = 1; + google.protobuf.Duration preferred_lifetime = 2; + google.protobuf.Duration valid_lifetime = 3; +} + message DhcpRequest { } -message DhcpResponse { - Error error = 1; +message DhcpResponse { + Error error = 1; + repeated Lease leases = 2; } diff --git a/tunnel/tools/libwg-go/Makefile b/tunnel/tools/libwg-go/Makefile index ba3ab19e..9ed24e1c 100644 --- a/tunnel/tools/libwg-go/Makefile +++ b/tunnel/tools/libwg-go/Makefile @@ -71,7 +71,7 @@ gen/%_grpc.pb.go: $(PROTODIR)/%.proto $(BUILDDIR)/go-$(GO_VERSION)/.prepared $(P $(PROTOC) -I $(PROTODIR) -I $(PROTO_INCLUDEDIR) --go-grpc_out=./gen --go-grpc_opt=paths=source_relative $< $(DESTDIR)/libwg-go.so: export PATH := $(BUILDDIR)/go-$(GO_VERSION)/bin/:$(PATH) -$(DESTDIR)/libwg-go.so: $(BUILDDIR)/go-$(GO_VERSION)/.prepared go.mod api-android.go http-proxy.go service.go gen/libwg.pb.go gen/libwg_grpc.pb.go jni.c +$(DESTDIR)/libwg-go.so: $(BUILDDIR)/go-$(GO_VERSION)/.prepared go.mod api-android.go dhcp.go http-proxy.go service.go gen/libwg.pb.go gen/libwg_grpc.pb.go jni.c go build -tags linux -ldflags="-X golang.zx2c4.com/wireguard/ipc.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard" -v -trimpath -o "$@" -buildmode c-shared .DELETE_ON_ERROR: diff --git a/tunnel/tools/libwg-go/api-android.go b/tunnel/tools/libwg-go/api-android.go index 0ab80be9..78ee7f54 100644 --- a/tunnel/tools/libwg-go/api-android.go +++ b/tunnel/tools/libwg-go/api-android.go @@ -49,6 +49,7 @@ type TunnelHandle struct { device *device.Device uapi net.Listener logger *device.Logger + tun *tun.NativeTun } var tunnelHandles map[int32]TunnelHandle @@ -148,10 +149,29 @@ 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, tun: tun} return i } +//export wgSetFd +func wgSetFd(tunnelHandle int32, tunFd int32) { + tag := cstring(fmt.Sprintf("WireGuard/GoBackend/%x", tunnelHandle)) + logger := &device.Logger{ + Verbosef: AndroidLogger{level: C.ANDROID_LOG_DEBUG, tag: tag}.Printf, + Errorf: AndroidLogger{level: C.ANDROID_LOG_ERROR, tag: tag}.Printf, + } + + handle, ok := tunnelHandles[tunnelHandle] + if !ok { + unix.Close(int(tunFd)) + logger.Errorf("Tunnel not found") + return + } + + handle.tun.SetFd(int(tunFd)) + logger.Verbosef("wgSetFd: %v", tunFd) +} + //export wgTurnOff func wgTurnOff(tunnelHandle int32) { handle, ok := tunnelHandles[tunnelHandle] diff --git a/tunnel/tools/libwg-go/dhcp.go b/tunnel/tools/libwg-go/dhcp.go index 70a95f27..aaeb2e38 100644 --- a/tunnel/tools/libwg-go/dhcp.go +++ b/tunnel/tools/libwg-go/dhcp.go @@ -1,73 +1,30 @@ package main import ( - "context" - "fmt" - "log" - "net" - "net/netip" - - "github.com/insomniacslk/dhcp/dhcpv4" - "github.com/insomniacslk/dhcp/dhcpv6" - "github.com/insomniacslk/dhcp/dhcpv6/nclient6" - "github.com/insomniacslk/dhcp/iana" + "context" + "net" + "net/netip" + + "github.com/insomniacslk/dhcp/dhcpv4" + "github.com/insomniacslk/dhcp/dhcpv6" + "github.com/insomniacslk/dhcp/dhcpv6/nclient6" + "github.com/insomniacslk/dhcp/iana" + + gen "golang.zx2c4.com/wireguard/android/gen" ) const ( - ENABLE_PD = false - //ENABLE_PD = true - ENABLE_TA = false - //ENABLE_TA = true - //ENABLE_4O6 = false - ENABLE_4O6 = true + ENABLE_PD = true ) -func withDHCPv4Msg(msg *dhcpv4.DHCPv4) dhcpv6.Modifier { - return func(d dhcpv6.DHCPv6) { - opt := dhcpv6.OptDHCPv4Msg{ - Msg: msg, - } - d.UpdateOption(&opt) - } -} - -func newDHCPv4Query(flags uint32, modifiers ...dhcpv6.Modifier) (*dhcpv6.Message, error) { - msg, err := dhcpv6.NewMessage() - if err != nil { - return nil, err - } - msg.MessageType = dhcpv6.MessageTypeDHCPv4Query - msg.TransactionID = dhcpv6.TransactionID{byte(flags >> 16), byte(flags >> 8), byte(flags)} - //msg.AddOption(dhcpv6.OptElapsedTime(0)) - - for _, mod := range modifiers { - mod(msg) - } - return msg, nil -} - -// type DHCPv4o6Client struct { -// nclient4.Client -// } - -func dhcpv4Query(ctx context.Context, client *nclient6.Client, dest *net.UDPAddr, msg *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, error) { - query, err := newDHCPv4Query(0x800000, withDHCPv4Msg(msg)) - if err != nil { - return nil, err - } - - resp, err := client.SendAndRead(ctx, dest, query, nil) - if err != nil { - return nil, err - } - - msgOpt := resp.GetOneOption(dhcpv6.OptionDHCPv4Msg) - if msgOpt == nil { - return nil, fmt.Errorf("Missing DHCPv4Msg option in response") - } - - offer := msgOpt.(*dhcpv6.OptDHCPv4Msg).Msg - return offer, nil +type dhcp struct { + fqdn string + hwAddr net.HardwareAddr + conn *net.UDPConn + serverAddr net.UDPAddr + client *nclient6.Client + linkAddr net.IP + peerAddr net.IP } func newClientIDOpt(duid *dhcpv6.Duid) dhcpv4.Option { @@ -78,171 +35,164 @@ func newClientIDOpt(duid *dhcpv6.Duid) dhcpv4.Option { return dhcpv4.OptClientIdentifier(ident) } -func runDhcp4o6(ctx context.Context, client *nclient6.Client, v4o6 *dhcpv6.OptDHCP4oDHCP6Server, duid *dhcpv6.Duid, hwAddr net.HardwareAddr, hostName string) (*netip.Prefix, error) { - serverAddr := nclient6.AllDHCPRelayAgentsAndServers - if len(v4o6.DHCP4oDHCP6Servers) > 0 { - // TODO use all servers - serverAddr = &net.UDPAddr{ - IP: v4o6.DHCP4oDHCP6Servers[0], - Port: dhcpv6.DefaultServerPort, - } - } - - modHostName := dhcpv4.WithGeneric(dhcpv4.OptionHostName, []byte(hostName)) - disc, err := dhcpv4.NewDiscovery(hwAddr, dhcpv4.WithOption(newClientIDOpt(duid)), modHostName) - if err != nil { - return nil, err - } - offer, err := dhcpv4Query(ctx, client, serverAddr, disc) - if err != nil { - return nil, err - } - req, err := dhcpv4.NewRequestFromOffer(offer, modHostName) - if err != nil { - return nil, err - } - ack, err := dhcpv4Query(ctx, client, serverAddr, req) - if err != nil { - return nil, err - } - - // client.Close() - fmt.Println("doClient end", ack.YourIPAddr, ack.SubnetMask()) - if ip4 := ack.YourIPAddr.To4(); ip4 == nil { - return nil, fmt.Errorf("Not IPv4! %v", ack.YourIPAddr) - } else { - log.Println("IPv4", ip4) +func getDuid(hwAddr net.HardwareAddr) dhcpv6.Duid { + duid := dhcpv6.Duid{ + Type: dhcpv6.DUID_LL, + HwType: iana.HWTypeEthernet, + LinkLayerAddr: hwAddr, } - addr, _ := netip.AddrFromSlice(ack.YourIPAddr) - prefix := netip.PrefixFrom(addr, 32) // TODO use SubnetMask - log.Printf("Append %v", prefix) - return &prefix, nil + return duid } -func RunDhcp(ctx context.Context, conn *net.UDPConn, hwAddr net.HardwareAddr) ([]netip.Prefix, error) { - client, err := nclient6.NewWithConn(conn, hwAddr, nclient6.WithDebugLogger()) +func (d *dhcp) encapSendAndRead(ctx context.Context, msg *dhcpv6.Message, match nclient6.Matcher) (*dhcpv6.Message, error) { + packet, err := dhcpv6.EncapsulateRelay(msg, dhcpv6.MessageTypeRelayForward, d.linkAddr, d.peerAddr) if err != nil { return nil, err } - duid := dhcpv6.Duid{ - Type: dhcpv6.DUID_LL, - HwType: iana.HWTypeEthernet, - LinkLayerAddr: hwAddr, + relay, err := d.client.SendAndReadRelay(ctx, &d.serverAddr, packet, match) + if err != nil { + return nil, err } - hostName := "gvisor" - fqdn := hostName + ".m7n.se" - fqdnOpt := dhcpv6.WithFQDN(0x1, fqdn) + inner, err := relay.GetInnerMessage() + if err != nil { + return nil, err + } - solicitMods := []dhcpv6.Modifier{} + return inner, nil +} - if ENABLE_PD { - _, iaIPNet, err := net.ParseCIDR("::/64") - if err != nil { - return nil, err +// isRelayMessageType returns a matcher that checks for the message type. +func isRelayMessageType(t dhcpv6.MessageType, tt ...dhcpv6.MessageType) nclient6.Matcher { + return func(p dhcpv6.DHCPv6) bool { + inner, err := p.GetInnerMessage() + if err != nil { + return false + } + if inner.Type() == t { + return true } - iaPrefix := dhcpv6.OptIAPrefix{ - 0, 0, iaIPNet, dhcpv6.PrefixOptions{}, + for _, mt := range tt { + if inner.Type() == mt { + return true + } } - solicitMods = append(solicitMods, dhcpv6.WithIAPD([4]byte{0, 0, 0, 1}, &iaPrefix)) + return false } +} - if ENABLE_TA { - solicitMods = append(solicitMods, dhcpv6.WithIATA([4]byte{0, 0, 0, 1})) - } - - oro := dhcpv6.WithRequestedOptions(dhcpv6.OptionDHCP4oDHCP6Server, dhcpv6.OptionFQDN) +// func New() *dhcp { +// } - solicitMods = append(solicitMods, dhcpv6.WithIAID([4]byte{0, 0, 0, 1})) - solicitMods = append(solicitMods, fqdnOpt) - solicitMods = append(solicitMods, dhcpv6.WithClientID(duid)) - solicitMods = append(solicitMods, oro) +func RunDhcp(ctx context.Context) ([]*gen.Lease, error) { + d := &dhcp{} + + d.linkAddr = net.ParseIP("fe80::101") + d.peerAddr = net.ParseIP("::1") - adv, err := client.Solicit(ctx, solicitMods...) - if err != nil { - return nil, err - } + hostName := "foobar" + d.fqdn = hostName + ".m7n.se" + d.hwAddr = []byte{41, 42, 43, 44, 45, 46} - requestMods := []dhcpv6.Modifier{} - requestMods = append(requestMods, fqdnOpt) - requestMods = append(requestMods, oro) + laddr, err := netip.ParseAddr("fd1c:a56b:a0d7:1260::101") + if err != nil { + return nil, err + } - if ENABLE_TA { - // Add IA_TA from advertise which is not added by default. - withIaTa := func(req dhcpv6.DHCPv6) { - if iaTa := adv.GetOneOption(dhcpv6.OptionIATA); iaTa != nil { - req.AddOption(iaTa) - } - } - requestMods = append(requestMods, withIaTa) - } + src := net.UDPAddr{IP: laddr.AsSlice(), + Port: 0, // Use non-restrict UDP source port + } - msg, err := client.Request(ctx, adv, requestMods...) - if err != nil { - return nil, err - } + raddr, err := netip.ParseAddr("fd1c:a56b:a0d7:1260::1") + if err != nil { + return nil, err + } - iana := msg.GetOneOption(dhcpv6.OptionIANA).(*dhcpv6.OptIANA) - ianaOpts := iana.Options.Get(dhcpv6.OptionIAAddr) + d.serverAddr = net.UDPAddr{IP: raddr.AsSlice(), + Port: 547, + } - var iapdOpts []dhcpv6.Option + err = d.Start(&src) + if err != nil { + return nil, err + } - if ENABLE_PD { - iapd := msg.GetOneOption(dhcpv6.OptionIAPD).(*dhcpv6.OptIAPD) - iapdOpts = iapd.Options.Get(dhcpv6.OptionIAPrefix) - } + defer d.Close() - addrs := make([]netip.Prefix, 0, len(ianaOpts)+len(iapdOpts)+1) + reply, err := d.ObtainLease(ctx) // Use reply + if err != nil { + return nil, err + } - opt4o6 := msg.GetOneOption(dhcpv6.OptionDHCP4oDHCP6Server) - if ENABLE_4O6 && opt4o6 != nil { - v4o6 := opt4o6.(*dhcpv6.OptDHCP4oDHCP6Server) - log.Println("dhcp4o6: ", v4o6) + return getAddressesFromReply(reply), nil +} - prefix, err := runDhcp4o6(ctx, client, v4o6, &duid, hwAddr, hostName) - if err == nil { - addrs = append(addrs, *prefix) - } else { - return nil, err - } - } +func getAddressesFromReply(reply *dhcpv6.Message) []*gen.Lease{ + var leases []*gen.Lease = make([]*gen.Lease, 0, 4) + + if opt := reply.GetOneOption(dhcpv6.OptionIANA); opt != nil { + iana := opt.(*dhcpv6.OptIANA) + ianaOpts := iana.Options.Get(dhcpv6.OptionIAAddr) + + for _, opt := range ianaOpts { + addr :=opt.(*dhcpv6.OptIAAddress).IPv6Addr + lease := &gen.Lease{ + Address: &gen.InetAddress{ + Address: addr, + }, + // PreferredLifetime: , + // ValidLifetime: , + } + leases = append(leases, lease) + } + } + + return leases +} - for _, addr := range ianaOpts { - prefix := netip.PrefixFrom(netip.IPv6Unspecified(), 128) - ip, ok := netip.AddrFromSlice(addr.(*dhcpv6.OptIAAddress).IPv6Addr) - if ok { - prefix = netip.PrefixFrom(ip, 128) - } +func (d *dhcp) Start(localAddr *net.UDPAddr) error { + conn, err := net.ListenUDP("udp6", localAddr) + if err != nil { + return err + } + + d.client, err = nclient6.NewWithConn(conn, d.hwAddr, nclient6.WithDebugLogger()) + return err +} - log.Printf("IANA %v", prefix) - addrs = append(addrs, prefix) - } +func (d *dhcp) Close() error { + err := d.client.Close() - for _, addr := range iapdOpts { - ipnet := addr.(*dhcpv6.OptIAPrefix).Prefix - addr, _ := netip.AddrFromSlice(ipnet.IP) // TODO use ok - prefixlen, _ := ipnet.Mask.Size() - prefix := netip.PrefixFrom(addr, prefixlen) + d.client = nil + return err +} - log.Printf("IAPD %v", prefix) - addrs = append(addrs, prefix) - } +func (d *dhcp) ObtainLease(ctx context.Context) (*dhcpv6.Message, error){ + duidOpt := dhcpv6.WithClientID(getDuid(d.hwAddr)) + fqdnOpt := dhcpv6.WithFQDN(0x1, d.fqdn) - opt := msg.GetOneOption(dhcpv6.OptionIATA) - if opt != nil { - iata := opt.(*dhcpv6.OptIATA) - taOpts := iata.Options.Get(dhcpv6.OptionIAAddr) + solicit, err := dhcpv6.NewSolicit(d.hwAddr, duidOpt, fqdnOpt, dhcpv6.WithRapidCommit) + if err != nil { + return nil, err + } - for _, addr := range taOpts { - ip := addr.(*dhcpv6.OptIAAddress).IPv6Addr - addr, _ := netip.AddrFromSlice(ip) // TODO use ok - prefix := netip.PrefixFrom(addr, 128) + msg, err := d.encapSendAndRead(ctx, solicit, isRelayMessageType(dhcpv6.MessageTypeReply, dhcpv6.MessageTypeAdvertise)) + if err != nil { + return nil, err + } - log.Printf("IATA %v", prefix) - } - } + if msg.Type() == dhcpv6.MessageTypeReply { + // We got RapidCommitted. + return msg, nil + } + + // We didn't get RapidCommitted. Request regular lease. + req, err := dhcpv6.NewRequestFromAdvertise(msg, fqdnOpt) + if err != nil { + return nil, err + } - return addrs, nil + return d.encapSendAndRead(ctx, req, nil) } diff --git a/tunnel/tools/libwg-go/go.mod b/tunnel/tools/libwg-go/go.mod index 19ee576f..e92d2cc0 100644 --- a/tunnel/tools/libwg-go/go.mod +++ b/tunnel/tools/libwg-go/go.mod @@ -22,3 +22,6 @@ require ( golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect ) + +replace github.com/insomniacslk/dhcp => /src/insomniacslk-dhcp +replace golang.zx2c4.com/wireguard => /src/wireguard-go diff --git a/tunnel/tools/libwg-go/jni.c b/tunnel/tools/libwg-go/jni.c index 121729a7..20bfb332 100644 --- a/tunnel/tools/libwg-go/jni.c +++ b/tunnel/tools/libwg-go/jni.c @@ -15,6 +15,7 @@ extern int wgGetSocketV6(int handle); extern char *wgGetConfig(int handle); extern char *wgVersion(); extern int wgStartGrpc(); +extern int wgSetFd(int handle, int tun_fd); JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOn(JNIEnv *env, jclass c, jstring ifname, jint tun_fd, jstring settings) { @@ -82,3 +83,8 @@ JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgStartGrpc( (*env)->ReleaseStringUTFChars(env, sockname, sockname_str); return res; } + +JNIEXPORT void JNICALL Java_com_wireguard_android_backend_GoBackend_wgSetFd(JNIEnv *env, jclass c, jint handle, jint tun_fd) +{ + wgSetFd(handle, tun_fd); +} diff --git a/tunnel/tools/libwg-go/service.go b/tunnel/tools/libwg-go/service.go index 1f2e629c..66bdd5ef 100644 --- a/tunnel/tools/libwg-go/service.go +++ b/tunnel/tools/libwg-go/service.go @@ -244,3 +244,20 @@ func (e *LibwgServiceImpl) IpcSet(ctx context.Context, req *gen.IpcSetRequest) ( return r, nil } + +func (e *LibwgServiceImpl) Dhcp(ctx context.Context, req *gen.DhcpRequest) (*gen.DhcpResponse, error) { + leases, err := RunDhcp(ctx) + if err != nil { + r := &gen.DhcpResponse{ + Error: &gen.Error{ + Message: fmt.Sprintf("RunDhcp failed: %v", err), + }, + } + return r, nil + } + + r := &gen.DhcpResponse{ + Leases: leases, + } + return r, nil +} |