/* SPDX-License-Identifier: Apache-2.0 * * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. */ package main // #cgo LDFLAGS: -llog // #include import "C" import ( "context" "fmt" "math" "net" "os" "os/signal" "runtime" "runtime/debug" "sort" "strings" "unsafe" "github.com/insomniacslk/dhcp/dhcpv4" "github.com/insomniacslk/dhcp/dhcpv6" "github.com/insomniacslk/dhcp/dhcpv6/nclient6" "github.com/insomniacslk/dhcp/iana" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/header" "gvisor.dev/gvisor/pkg/tcpip/link/fdbased" "gvisor.dev/gvisor/pkg/tcpip/link/sniffer" "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" "gvisor.dev/gvisor/pkg/tcpip/transport/udp" "gvisor.dev/gvisor/pkg/tcpip/stack" "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" ) 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 stack *stack.Stack tnet *netstack.Net logger *device.Logger interfaceName string tunFd int linkEP stack.LinkEndpoint wgNic tcpip.NICID tunNic tcpip.NICID } var tcpipStack *stack.Stack var nextNic tcpip.NICID = 1 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) 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, } handle, tun, err := newTunnel(logger) if err != nil { logger.Errorf("newTunnel: %v", err) return -1 } handle.interfaceName = interfaceName handle.tunFd = int(tunFd) name := "foobar" 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 } handle.device = device handle.uapi = uapi tunnelHandles[i] = handle go handle.startDHCPv6() return i } //export wgTurnOff func wgTurnOff(tunnelHandle int32) { handle, ok := tunnelHandles[tunnelHandle] if !ok { return } handle.logger.Verbosef("wgTurnOff enter") delete(tunnelHandles, tunnelHandle) if handle.uapi != nil { handle.uapi.Close() } handle.device.Close() handle.logger.Verbosef("wgTurnOff before Close") handle.stack.RemoveNIC(handle.wgNic) handle.stack.RemoveNIC(handle.tunNic) unix.Close(handle.tunFd) handle.logger.Verbosef("wgTurnOff exit") } //export wgGetSocketV4 func wgGetSocketV4(tunnelHandle int32) int32 { handle, ok := tunnelHandles[tunnelHandle] if !ok { return -1 } 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 } 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 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)) //modifiers = append([]dhcpv6.Modifier{dhcpv6.WithRequestedOptions(dhcpv6.OptionDHCP4oDHCP6Server)}, modifiers...) for _, mod := range modifiers { mod(msg) } return msg, nil } func parseIP(s string) tcpip.Address { addr := tcpip.Address(net.ParseIP(s)) ip4addr := addr.To4(); if ip4addr != "" { return ip4addr } else { return addr } } func newTunnel(logger *device.Logger) (TunnelHandle, tun.Device, error) { // TODO: Configurable local address localAddresses := []net.IP{net.ParseIP("fe80::104")} dnsServers := []net.IP{net.ParseIP("8.8.8.8"), net.ParseIP("8.8.4.4")} mtu := 1420 if tcpipStack == nil { opts := stack.Options{ NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol}, HandleLocal: true, } tcpipStack = stack.New(opts) } nic := nextNic; nextNic = nextNic + 1 tun, tnet, err := netstack.CreateNetTUNWithStack(tcpipStack, nic, localAddresses, dnsServers, mtu) if err != nil { // TODO: Release stack? return TunnelHandle{}, nil, err } handle := TunnelHandle{stack: tcpipStack, tnet: tnet, logger: logger, wgNic: nic} return handle, tun, nil } func (handle *TunnelHandle) startDHCPv6() { // TODO: Configurable address address := fmt.Sprintf("[fe80::104%%%d]:546", handle.wgNic) src, err := net.ResolveUDPAddr("udp6", address); if err != nil { handle.logger.Errorf("ResolveUDPAddr: %v", err) return } var dst *net.UDPAddr dst = nil conn, err := handle.tnet.DialUDP(src, dst); if err != nil { handle.logger.Errorf("DialUDP: (%v -> %v) %v", src, dst, err) return } hwaddr := []byte("ABCDEF") client, err := nclient6.NewWithConn(conn, hwaddr, nclient6.WithDebugLogger()); if err != nil { handle.logger.Errorf("NewWithConn: %v", err) return } defer func() { err := client.Close() if err != nil { handle.logger.Errorf("Close: %v", err) } handle.logger.Verbosef("Closed") }() duid := dhcpv6.Duid{ Type: dhcpv6.DUID_LL, HwType: iana.HWTypeEthernet, LinkLayerAddr: hwaddr, } hostName := "gvisor" fqdn := hostName + ".m7n.se" fqdnOpt := dhcpv6.WithFQDN(0x1, fqdn) _, iaIPNet, err := net.ParseCIDR("::/64"); if err != nil { handle.logger.Errorf("ParseCIDR: %v", err) return } iaPrefix := dhcpv6.OptIAPrefix{ 0, 0, iaIPNet, dhcpv6.PrefixOptions{}, } iaid := []byte{0, 0, 0, 3} ident := []byte{255} // Type IAID+DUID ident = append(ident, iaid...) // IAID ident = append(ident, duid.ToBytes()...) // DUID clientIDOpt := dhcpv4.OptClientIdentifier(ident) adv, err := client.Solicit(context.Background(), dhcpv6.WithIAPD([4]byte{0, 0, 0, 1}, &iaPrefix), dhcpv6.WithIAID([4]byte{0, 0, 0, 2}), fqdnOpt, dhcpv6.WithClientID(duid), dhcpv6.WithRequestedOptions(dhcpv6.OptionDHCP4oDHCP6Server), dhcpv6.WithDHCP4oDHCP6Server(net.ParseIP("fe80::1")), ); if err != nil { handle.logger.Errorf("Solicit: %v", err) return } msg, err := client.Request(context.Background(), adv, fqdnOpt); if err != nil { handle.logger.Errorf("Request: %v", err) return } modHostName := dhcpv4.WithGeneric(dhcpv4.OptionHostName, []byte(hostName)) disc, err := dhcpv4.NewDiscovery(hwaddr, dhcpv4.WithOption(clientIDOpt), modHostName); if err != nil { handle.logger.Errorf("NewDiscovery: %v", err) return } disc_query, err := NewDHCPv4Query(0x800000, withDHCPv4Msg(disc)); if err != nil { handle.logger.Errorf("NewDHCPv4Query: %v", err) return } serverAddr := nclient6.AllDHCPRelayAgentsAndServers disc_resp, err := client.SendAndRead(context.Background(), serverAddr, disc_query, nil); if err != nil { handle.logger.Errorf("SendAndRead: %v", err) return } msgOpt := disc_resp.GetOneOption(dhcpv6.OptionDHCPv4Msg); if msgOpt == nil { handle.logger.Errorf("Missing DHCPv4Msg option in discovery") return } offer := msgOpt.(*dhcpv6.OptDHCPv4Msg).Msg req, err := dhcpv4.NewRequestFromOffer(offer, modHostName); if err != nil { handle.logger.Errorf("NewRequestFromOffer: %v", err) return } req_query, err := NewDHCPv4Query(0x800000, withDHCPv4Msg(req)); if err != nil { handle.logger.Errorf("NewDHCPv4Query: %v", err) return } req_resp, err := client.SendAndRead(context.Background(), serverAddr, req_query, nil); if err != nil { handle.logger.Errorf("Serveraddr: %v", err) return } msgOpt = req_resp.GetOneOption(dhcpv6.OptionDHCPv4Msg); if msgOpt == nil { handle.logger.Errorf("Missing DHCPv4Msg option in ack") return } ack := msgOpt.(*dhcpv6.OptDHCPv4Msg).Msg handle.logger.Verbosef("DHCP done") var nic tcpip.NICID = nextNic; nextNic = nextNic + 1 handle.createNetTUNFromFD(nic) // TODO: Close clients? // client.Close() handle.logger.Verbosef("doClient end %v %v", ack.YourIPAddr, ack.SubnetMask()) if ip4 := ack.YourIPAddr.To4(); ip4 != nil { handle.logger.Errorf("IPv4! %v", ack.YourIPAddr) addr := tcpip.Address(ack.YourIPAddr) subnet, err := tcpip.NewSubnet( addr, tcpip.AddressMask(parseIP("255.255.255.255"))) if err != nil { handle.logger.Errorf("NewSubnet: %v", err) return } // The TUN interface need a link address for some reason linkAddr := tcpip.Address(parseIP("169.254.0.1")) tcperr := handle.stack.AddAddress(nic, ipv4.ProtocolNumber, linkAddr) if tcperr != nil { handle.logger.Errorf("AddAddress: %v", tcperr) return } handle.stack.AddRoute(tcpip.Route{ Destination: subnet, NIC: nic, }) handle.stack.AddRoute(tcpip.Route{ Destination: header.IPv4EmptySubnet, NIC: handle.wgNic, }) } // tcperr := tnet.AddAddress(ack.YourIPAddr); if tcperr != nil { // log.Fatal(tcperr) // } // Maybe // The TUN interface need a link address for some reason linkAddr := tcpip.Address(parseIP("fe80::104")) tcperr := handle.stack.AddAddress(nic, ipv6.ProtocolNumber, linkAddr) if tcperr != nil { handle.logger.Errorf("AddAddress: %v", tcperr) return } handle.stack.AddRoute(tcpip.Route{ Destination: header.IPv6EmptySubnet, NIC: handle.wgNic, }) iana := msg.GetOneOption(dhcpv6.OptionIANA).(*dhcpv6.OptIANA) for _, addr := range iana.Options.Get(dhcpv6.OptionIAAddr) { ip := addr.(*dhcpv6.OptIAAddress).IPv6Addr str := ip.String() + "/128" handle.logger.Verbosef("Addr %v", str) addr := tcpip.Address(ip) subnet, err := tcpip.NewSubnet( addr, tcpip.AddressMask(parseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))) if err != nil { handle.logger.Errorf("NewSubnet: %v", err) return } handle.stack.AddRoute(tcpip.Route{ Destination: subnet, NIC: nic, }) } // Sort route table for longest prefix match routes := handle.stack.GetRouteTable() sort.Slice(routes, func(i, j int) bool { return routes[i].Destination.Prefix() > routes[j].Destination.Prefix() }) handle.stack.SetRouteTable(routes) handle.stack.SetForwarding(ipv4.ProtocolNumber, true) handle.stack.SetForwarding(ipv6.ProtocolNumber, true) handle.logger.Verbosef("DHCP addresses set up") // TODO update WireGuard settings } func (handle *TunnelHandle) newTun(nic tcpip.NICID) error { err := unix.SetNonblock(handle.tunFd, true) if err != nil { return err } var mtu uint32 = 1420 handle.linkEP, err = fdbased.New(&fdbased.Options{FDs: []int{handle.tunFd}, MTU: mtu}) if err != nil { handle.logger.Errorf("fdbased.New: %v", err) return err } if err := handle.stack.CreateNIC(nic, sniffer.New(handle.linkEP)); err != nil { handle.logger.Errorf("CreateNIC: %v", err) // TODO: Return error return nil } handle.tunNic = nic return nil } func (handle *TunnelHandle) createNetTUNFromFD(nic tcpip.NICID) { err := handle.newTun(nic) if err != nil { handle.logger.Errorf("newTun: %v", err) return } } func main() {}