summaryrefslogtreecommitdiffhomepage
path: root/tunnel
diff options
context:
space:
mode:
authorMikael Magnusson <mikma@users.sourceforge.net>2023-02-14 02:58:39 +0100
committerMikael Magnusson <mikma@users.sourceforge.net>2023-03-23 22:32:11 +0100
commitdee4d2ba7ef2a8643d75e41a162e1e03bd91ed6e (patch)
treef0672b899bf6fd4c77d0914501a5185959cef615 /tunnel
parente6a8901298928315ff11c0407533994548a04ecd (diff)
WIP: dhcp dev
Turn off blocks
Diffstat (limited to 'tunnel')
-rw-r--r--tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java214
-rw-r--r--tunnel/src/main/proto/libwg.proto13
-rw-r--r--tunnel/tools/libwg-go/Makefile2
-rw-r--r--tunnel/tools/libwg-go/api-android.go22
-rw-r--r--tunnel/tools/libwg-go/dhcp.go338
-rw-r--r--tunnel/tools/libwg-go/go.mod3
-rw-r--r--tunnel/tools/libwg-go/jni.c6
-rw-r--r--tunnel/tools/libwg-go/service.go17
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
+}