diff options
author | Mikael Magnusson <mikma@users.sourceforge.net> | 2023-02-11 22:24:31 +0100 |
---|---|---|
committer | Mikael Magnusson <mikma@users.sourceforge.net> | 2023-09-27 23:59:41 +0200 |
commit | 4d17701f7d13bf63cb3e10ea5fa70db38721b64f (patch) | |
tree | f9d89f317ad668e42922c2f1176621f2003f1630 /tunnel/tools/libwg-go | |
parent | b8adc5e5fb9fae80c1c3f6cdb80ee81dfe1eb32d (diff) |
tunnel: request DHCPv6 leases
Diffstat (limited to 'tunnel/tools/libwg-go')
-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 | 192 | ||||
-rw-r--r-- | tunnel/tools/libwg-go/jni.c | 6 | ||||
-rw-r--r-- | tunnel/tools/libwg-go/service.go | 63 |
5 files changed, 283 insertions, 2 deletions
diff --git a/tunnel/tools/libwg-go/Makefile b/tunnel/tools/libwg-go/Makefile index 9bec6fdc..5ae4548d 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 -buildid=" -v -trimpath -buildvcs=false -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 484a89b0..943ba628 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 @@ -143,10 +144,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 new file mode 100644 index 00000000..b7919151 --- /dev/null +++ b/tunnel/tools/libwg-go/dhcp.go @@ -0,0 +1,192 @@ +package main + +import ( + "context" + "net" + "net/netip" + "time" + + "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 = true +) + +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 { + iaid := []byte{0, 0, 0, 3} + ident := []byte{255} // Type IAID+DUID + ident = append(ident, iaid...) // IAID + ident = append(ident, duid.ToBytes()...) // DUID + return dhcpv4.OptClientIdentifier(ident) +} + +func getDuid(hwAddr net.HardwareAddr) dhcpv6.DUID { + duid := &dhcpv6.DUIDLLT{ + HWType: iana.HWTypeEthernet, + Time: uint32(time.Now().Unix()), + LinkLayerAddr: hwAddr, + } + + return duid +} + +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 + } + + relay, err := d.client.SendAndReadRelay(ctx, &d.serverAddr, packet, match) + if err != nil { + return nil, err + } + + inner, err := relay.GetInnerMessage() + if err != nil { + return nil, err + } + + return inner, nil +} + +// 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 + } + for _, mt := range tt { + if inner.Type() == mt { + return true + } + } + return false + } +} + +// func New() *dhcp { +// } + +func RunDhcp(ctx context.Context, laddr, raddr netip.Addr) ([]*gen.Lease, error) { + d := &dhcp{} + + d.linkAddr = net.ParseIP("fe80::101") + d.peerAddr = net.ParseIP("::1") + + // TODO generate hostname and hwAddr from public key + hostName := "foobar" + d.fqdn = hostName + ".m7n.se" + d.hwAddr = []byte{41, 42, 43, 44, 45, 46} + + + src := net.UDPAddr{IP: laddr.AsSlice(), + Port: 0, // Use non-restrict UDP source port + } + + + d.serverAddr = net.UDPAddr{IP: raddr.AsSlice(), + Port: 547, + } + + err := d.Start(&src) + if err != nil { + return nil, err + } + + defer d.Close() + + reply, err := d.ObtainLease(ctx) // Use reply + if err != nil { + return nil, err + } + + return getAddressesFromReply(reply), nil +} + +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 +} + +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 +} + +func (d *dhcp) Close() error { + err := d.client.Close() + + d.client = nil + return err +} + +func (d *dhcp) ObtainLease(ctx context.Context) (*dhcpv6.Message, error){ + duidOpt := dhcpv6.WithClientID(getDuid(d.hwAddr)) + fqdnOpt := dhcpv6.WithFQDN(0x1, d.fqdn) + + solicit, err := dhcpv6.NewSolicit(d.hwAddr, duidOpt, fqdnOpt, dhcpv6.WithRapidCommit) + if err != nil { + return nil, err + } + + msg, err := d.encapSendAndRead(ctx, solicit, isRelayMessageType(dhcpv6.MessageTypeReply, dhcpv6.MessageTypeAdvertise)) + if err != nil { + return nil, err + } + + 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 d.encapSendAndRead(ctx, req, nil) +} diff --git a/tunnel/tools/libwg-go/jni.c b/tunnel/tools/libwg-go/jni.c index b386579c..443848e8 100644 --- a/tunnel/tools/libwg-go/jni.c +++ b/tunnel/tools/libwg-go/jni.c @@ -16,6 +16,7 @@ extern char *wgGetConfig(int handle); extern char *wgVersion(); extern int wgSetConfig(int handle, struct go_string settings); 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) { @@ -95,3 +96,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 72916a8e..30fe650b 100644 --- a/tunnel/tools/libwg-go/service.go +++ b/tunnel/tools/libwg-go/service.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "net" + "net/netip" "net/url" "os" @@ -224,3 +225,65 @@ func (e *LibwgServiceImpl) Reverse(stream gen.Libwg_ReverseServer) error { e.logger.Verbosef("Reverse returns") return nil } + +func (e *LibwgServiceImpl) Dhcp(ctx context.Context, req *gen.DhcpRequest) (*gen.DhcpResponse, error) { + var relayAddr netip.Addr + var sourceAddr netip.Addr + + source := req.GetSource() + if source != nil { + sourceAddr, _ = netip.AddrFromSlice(source.GetAddress()) + } + + if !sourceAddr.IsValid() || !sourceAddr.Is6() { + r := &gen.DhcpResponse{ + Error: &gen.Error{ + Message: fmt.Sprintf("DHCPv6 source address missing"), + }, + } + return r, nil + } + + relay := req.GetRelay() + if relay != nil { + relayAddr, _ = netip.AddrFromSlice(relay.GetAddress()) + } else { + // Construct relay address from source prefix + relayRaw := source.GetAddress()[:8] + relayRaw = append(relayRaw, 0) + relayRaw = append(relayRaw, 0) + relayRaw = append(relayRaw, 0) + relayRaw = append(relayRaw, 0) + relayRaw = append(relayRaw, 0) + relayRaw = append(relayRaw, 0) + relayRaw = append(relayRaw, 0) + relayRaw = append(relayRaw, 1) + relayAddr, _ = netip.AddrFromSlice(relayRaw) + } + + if !relayAddr.IsValid() || !relayAddr.Is6() { + r := &gen.DhcpResponse{ + Error: &gen.Error{ + Message: fmt.Sprintf("DHCPv6 relay address calculation failed"), + }, + } + return r, nil + } + + e.logger.Verbosef("RunDhcp %v %v", sourceAddr, relayAddr) + + leases, err := RunDhcp(ctx, sourceAddr, relayAddr) + 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 +} |