/* SPDX-License-Identifier: Apache-2.0 * * Copyright © 2017-2022 Jason A. Donenfeld . All Rights Reserved. */ package main // #cgo LDFLAGS: -llog // #include // extern void wgOnEvent(void *eventHandler, const char *event); import "C" import ( "context" "fmt" "log" "math" "net" "os" "os/signal" "runtime" "runtime/debug" "strings" "unsafe" "golang.org/x/sys/unix" "golang.zx2c4.com/wireguard/conn" "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/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 { level C.int tag *C.char } func cstring(s string) *C.char { b, err := unix.BytePtrFromString(s) if err != nil { b := [1]C.char{} return &b[0] } return (*C.char)(unsafe.Pointer(b)) } func (l AndroidLogger) Printf(format string, args ...interface{}) { C.__android_log_write(l.level, l.tag, cstring(fmt.Sprintf(format, args...))) } type TunnelHandle struct { device *device.Device uapi net.Listener eventHandler unsafe.Pointer cancel context.CancelFunc master *ipvlan.IPVLANMaster } var tunnelHandles map[int32]TunnelHandle func init() { tunnelHandles = make(map[int32]TunnelHandle) signals := make(chan os.Signal) signal.Notify(signals, unix.SIGUSR2) go func() { buf := make([]byte, os.Getpagesize()) for { select { case <-signals: n := runtime.Stack(buf, true) if n == len(buf) { n-- } buf[n] = 0 C.__android_log_write(C.ANDROID_LOG_ERROR, cstring("WireGuard/GoBackend/Stacktrace"), (*C.char)(unsafe.Pointer(&buf[0]))) } } }() } //export wgTurnOn 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, Errorf: AndroidLogger{level: C.ANDROID_LOG_ERROR, tag: tag}.Printf, } tun, name, err := tun.CreateUnmonitoredTUNFromFD(int(tunFd)) if err != nil { unix.Close(int(tunFd)) logger.Errorf("CreateUnmonitoredTUNFromFD: %v", err) return -1 } logger.Verbosef("Attaching to interface %v", name) device := device.NewDevice(tun, conn.NewStdNetBind(), logger) err = device.IpcSet(settings) if err != nil { unix.Close(int(tunFd)) 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 } 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 } 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, } opts := stack.Options{ NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol}, HandleLocal: true, } stack := stack.New(opts) // 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 } logger.Verbosef("DHCP finished") for _, addr := range addrs { logger.Verbosef("Address: %v", addr) } // TODO move to wgSetTunFd // tunEP := newTun(stack, 2, "tun0") // master.SetIntEP(tunEP) }(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 wgTurnOff func wgTurnOff(tunnelHandle int32) unsafe.Pointer { handle, ok := tunnelHandles[tunnelHandle] if !ok { return nil } 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 func wgGetSocketV4(tunnelHandle int32) int32 { handle, ok := tunnelHandles[tunnelHandle] if !ok { return -1 } C.wgOnEvent(handle.eventHandler, C.CString("Called wgGetSocketV4")) bind, _ := handle.device.Bind().(conn.PeekLookAtSocketFd) if bind == nil { return -1 } fd, err := bind.PeekLookAtSocketFd4() if err != nil { return -1 } return int32(fd) } //export wgGetSocketV6 func wgGetSocketV6(tunnelHandle int32) int32 { handle, ok := tunnelHandles[tunnelHandle] if !ok { return -1 } C.wgOnEvent(handle.eventHandler, C.CString("Called wgGetSocketV6")) bind, _ := handle.device.Bind().(conn.PeekLookAtSocketFd) if bind == nil { return -1 } fd, err := bind.PeekLookAtSocketFd6() if err != nil { return -1 } return int32(fd) } //export wgGetConfig func wgGetConfig(tunnelHandle int32) *C.char { handle, ok := tunnelHandles[tunnelHandle] if !ok { return nil } settings, err := handle.device.IpcGet() if err != nil { return nil } return C.CString(settings) } //export wgVersion func wgVersion() *C.char { info, ok := debug.ReadBuildInfo() if !ok { return C.CString("unknown") } for _, dep := range info.Deps { if dep.Path == "golang.zx2c4.com/wireguard" { parts := strings.Split(dep.Version, "-") if len(parts) == 3 && len(parts[2]) == 12 { return C.CString(parts[2][:7]) } return C.CString(dep.Version) } } return C.CString("unknown") } func main() {}