Merge branch 'develop' into nodeconfig

This commit is contained in:
Neil Alexander 2019-01-14 14:01:38 +00:00
commit 738a9da796
No known key found for this signature in database
GPG key ID: A02A2019A2BB0944
22 changed files with 734 additions and 85 deletions

View file

@ -1,17 +1,8 @@
package yggdrasil
// Defines the minimum required functions for an adapter type.
type AdapterInterface interface {
init(core *Core, send chan<- []byte, recv <-chan []byte)
read() error
write() error
close() error
}
// Defines the minimum required struct members for an adapter type (this is
// now the base type for tunAdapter in tun.go)
type Adapter struct {
AdapterInterface
core *Core
send chan<- []byte
recv <-chan []byte

148
src/yggdrasil/awdl.go Normal file
View file

@ -0,0 +1,148 @@
package yggdrasil
import (
"errors"
"fmt"
"sync"
"sync/atomic"
"time"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/util"
)
type awdl struct {
core *Core
mutex sync.RWMutex // protects interfaces below
interfaces map[string]*awdlInterface
}
type awdlInterface struct {
awdl *awdl
fromAWDL chan []byte
toAWDL chan []byte
shutdown chan bool
peer *peer
}
func (l *awdl) init(c *Core) error {
l.core = c
l.mutex.Lock()
l.interfaces = make(map[string]*awdlInterface)
l.mutex.Unlock()
return nil
}
func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte /*boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey*/, name string) (*awdlInterface, error) {
intf := awdlInterface{
awdl: l,
fromAWDL: fromAWDL,
toAWDL: toAWDL,
shutdown: make(chan bool),
}
l.mutex.Lock()
l.interfaces[name] = &intf
l.mutex.Unlock()
myLinkPub, myLinkPriv := crypto.NewBoxKeys()
meta := version_getBaseMetadata()
meta.box = l.core.boxPub
meta.sig = l.core.sigPub
meta.link = *myLinkPub
metaBytes := meta.encode()
l.core.log.Println("toAWDL <- metaBytes")
toAWDL <- metaBytes
l.core.log.Println("metaBytes = <-fromAWDL")
metaBytes = <-fromAWDL
l.core.log.Println("version_metadata{}")
meta = version_metadata{}
if !meta.decode(metaBytes) || !meta.check() {
return nil, errors.New("Metadata decode failure")
}
l.core.log.Println("version_getBaseMetadata{}")
base := version_getBaseMetadata()
if meta.ver > base.ver || meta.ver == base.ver && meta.minorVer > base.minorVer {
return nil, errors.New("Failed to connect to node: " + name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer))
}
l.core.log.Println("crypto.GetSharedKey")
shared := crypto.GetSharedKey(myLinkPriv, &meta.link)
//shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey)
l.core.log.Println("l.core.peers.newPeer")
intf.peer = l.core.peers.newPeer(&meta.box, &meta.sig, shared, name)
if intf.peer != nil {
intf.peer.linkOut = make(chan []byte, 1) // protocol traffic
intf.peer.out = func(msg []byte) {
defer func() { recover() }()
intf.toAWDL <- msg
} // called by peer.sendPacket()
l.core.switchTable.idleIn <- intf.peer.port // notify switch that we're idle
intf.peer.close = func() {
close(intf.fromAWDL)
close(intf.toAWDL)
}
go intf.handler()
go intf.peer.linkLoop()
return &intf, nil
}
delete(l.interfaces, name)
return nil, errors.New("l.core.peers.newPeer failed")
}
func (l *awdl) getInterface(identity string) *awdlInterface {
l.mutex.RLock()
defer l.mutex.RUnlock()
if intf, ok := l.interfaces[identity]; ok {
return intf
}
return nil
}
func (l *awdl) shutdown(identity string) error {
if intf, ok := l.interfaces[identity]; ok {
intf.shutdown <- true
l.core.peers.removePeer(intf.peer.port)
l.mutex.Lock()
delete(l.interfaces, identity)
l.mutex.Unlock()
return nil
} else {
return errors.New(fmt.Sprintf("Interface '%s' doesn't exist or already shutdown", identity))
}
}
func (ai *awdlInterface) handler() {
send := func(msg []byte) {
ai.toAWDL <- msg
atomic.AddUint64(&ai.peer.bytesSent, uint64(len(msg)))
util.PutBytes(msg)
}
for {
timerInterval := tcp_ping_interval
timer := time.NewTimer(timerInterval)
defer timer.Stop()
select {
case p := <-ai.peer.linkOut:
send(p)
continue
default:
}
timer.Stop()
select {
case <-timer.C:
default:
}
timer.Reset(timerInterval)
select {
case _ = <-timer.C:
send([]byte{})
case p := <-ai.peer.linkOut:
send(p)
continue
case r := <-ai.fromAWDL:
ai.peer.handlePacket(r)
ai.awdl.core.switchTable.idleIn <- ai.peer.port
case <-ai.shutdown:
return
}
}
}

View file

@ -46,6 +46,7 @@ type Core struct {
multicast multicast
nodeinfo nodeinfo
tcp tcpInterface
awdl awdl
log *log.Logger
ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this
}
@ -171,6 +172,16 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
c.nodeinfo.setNodeInfo(nc.NodeInfo, nc.NodeInfoPrivacy)
if err := c.tcp.init(c); err != nil {
c.log.Println("Failed to start TCP interface")
return err
}
if err := c.awdl.init(c); err != nil {
c.log.Println("Failed to start AWDL interface")
return err
}
if nc.SwitchOptions.MaxTotalQueueSize >= SwitchQueueTotalMinSize {
c.switchTable.queueTotalMaxSize = nc.SwitchOptions.MaxTotalQueueSize
}

143
src/yggdrasil/mobile.go Normal file
View file

@ -0,0 +1,143 @@
// +build mobile
package yggdrasil
import (
"encoding/hex"
"encoding/json"
"log"
"os"
"regexp"
"time"
hjson "github.com/hjson/hjson-go"
"github.com/mitchellh/mapstructure"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/util"
)
// This file is meant to "plug the gap" for mobile support, as Gomobile will
// not create headers for Swift/Obj-C etc if they have complex (non-native)
// types. Therefore for iOS we will expose some nice simple functions. Note
// that in the case of iOS we handle reading/writing to/from TUN in Swift
// therefore we use the "dummy" TUN interface instead.
func (c *Core) addStaticPeers(cfg *config.NodeConfig) {
if len(cfg.Peers) == 0 && len(cfg.InterfacePeers) == 0 {
return
}
for {
for _, peer := range cfg.Peers {
c.AddPeer(peer, "")
time.Sleep(time.Second)
}
for intf, intfpeers := range cfg.InterfacePeers {
for _, peer := range intfpeers {
c.AddPeer(peer, intf)
time.Sleep(time.Second)
}
}
time.Sleep(time.Minute)
}
}
// Starts a node with a randomly generated config.
func (c *Core) StartAutoconfigure() error {
mobilelog := MobileLogger{}
logger := log.New(mobilelog, "", 0)
nc := config.GenerateConfig(true)
nc.IfName = "dummy"
nc.AdminListen = "tcp://localhost:9001"
nc.Peers = []string{}
if hostname, err := os.Hostname(); err == nil {
nc.NodeInfo = map[string]interface{}{"name": hostname}
}
ifceExpr, err := regexp.Compile(".*")
if err == nil {
c.ifceExpr = append(c.ifceExpr, ifceExpr)
}
if err := c.Start(nc, logger); err != nil {
return err
}
go c.addStaticPeers(nc)
return nil
}
// Starts a node with the given JSON config. You can get JSON config (rather
// than HJSON) by using the GenerateConfigJSON() function.
func (c *Core) StartJSON(configjson []byte) error {
mobilelog := MobileLogger{}
logger := log.New(mobilelog, "", 0)
nc := config.GenerateConfig(false)
var dat map[string]interface{}
if err := hjson.Unmarshal(configjson, &dat); err != nil {
return err
}
if err := mapstructure.Decode(dat, &nc); err != nil {
return err
}
nc.IfName = "dummy"
//c.log.Println(nc.MulticastInterfaces)
for _, ll := range nc.MulticastInterfaces {
//c.log.Println("Processing MC", ll)
ifceExpr, err := regexp.Compile(ll)
if err != nil {
panic(err)
}
c.AddMulticastInterfaceExpr(ifceExpr)
}
if err := c.Start(nc, logger); err != nil {
return err
}
go c.addStaticPeers(nc)
return nil
}
// Generates mobile-friendly configuration in JSON format.
func GenerateConfigJSON() []byte {
nc := config.GenerateConfig(false)
nc.IfName = "dummy"
if json, err := json.Marshal(nc); err == nil {
return json
} else {
return nil
}
}
// Gets the node's IPv6 address.
func (c *Core) GetAddressString() string {
return c.GetAddress().String()
}
// Gets the node's IPv6 subnet in CIDR notation.
func (c *Core) GetSubnetString() string {
return c.GetSubnet().String()
}
// Gets the node's public encryption key.
func (c *Core) GetBoxPubKeyString() string {
return hex.EncodeToString(c.boxPub[:])
}
// Gets the node's public signing key.
func (c *Core) GetSigPubKeyString() string {
return hex.EncodeToString(c.sigPub[:])
}
// Wait for a packet from the router. You will use this when implementing a
// dummy adapter in place of real TUN - when this call returns a packet, you
// will probably want to give it to the OS to write to TUN.
func (c *Core) RouterRecvPacket() ([]byte, error) {
packet := <-c.router.tun.recv
return packet, nil
}
// Send a packet to the router. You will use this when implementing a
// dummy adapter in place of real TUN - when the operating system tells you
// that a new packet is available from TUN, call this function to give it to
// Yggdrasil.
func (c *Core) RouterSendPacket(buf []byte) error {
packet := append(util.GetBytes(), buf[:]...)
c.router.tun.send <- packet
return nil
}

View file

@ -0,0 +1,12 @@
// +build android
package yggdrasil
import "log"
type MobileLogger struct{}
func (nsl MobileLogger) Write(p []byte) (n int, err error) {
log.Println(string(p))
return len(p), nil
}

View file

@ -0,0 +1,68 @@
// +build mobile,darwin
package yggdrasil
/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation
#import <Foundation/Foundation.h>
void Log(const char *text) {
NSString *nss = [NSString stringWithUTF8String:text];
NSLog(@"%@", nss);
}
*/
import "C"
import (
"errors"
"unsafe"
"github.com/yggdrasil-network/yggdrasil-go/src/util"
)
type MobileLogger struct {
}
func (nsl MobileLogger) Write(p []byte) (n int, err error) {
p = append(p, 0)
cstr := (*C.char)(unsafe.Pointer(&p[0]))
C.Log(cstr)
return len(p), nil
}
func (c *Core) AWDLCreateInterface(name string) error {
fromAWDL := make(chan []byte, 32)
toAWDL := make(chan []byte, 32)
if intf, err := c.awdl.create(fromAWDL, toAWDL, name); err == nil {
if intf != nil {
c.log.Println(err)
return err
} else {
c.log.Println("c.awdl.create didn't return an interface")
return errors.New("c.awdl.create didn't return an interface")
}
} else {
c.log.Println(err)
return err
}
}
func (c *Core) AWDLShutdownInterface(name string) error {
return c.awdl.shutdown(name)
}
func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) {
if intf := c.awdl.getInterface(identity); intf != nil {
return <-intf.toAWDL, nil
}
return nil, errors.New("AWDLRecvPacket identity not known: " + identity)
}
func (c *Core) AWDLSendPacket(identity string, buf []byte) error {
packet := append(util.GetBytes(), buf[:]...)
if intf := c.awdl.getInterface(identity); intf != nil {
intf.fromAWDL <- packet
return nil
}
return errors.New("AWDLSendPacket identity not known: " + identity)
}

View file

@ -177,6 +177,6 @@ func (m *multicast) listen() {
}
addr.Zone = from.Zone
saddr := addr.String()
m.core.tcp.connect(saddr, "")
m.core.tcp.connect(saddr, addr.Zone)
}
}

View file

@ -0,0 +1,28 @@
// +build darwin
package yggdrasil
import "syscall"
import "golang.org/x/sys/unix"
func multicastReuse(network string, address string, c syscall.RawConn) error {
var control error
var reuseport error
var recvanyif error
control = c.Control(func(fd uintptr) {
reuseport = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
// sys/socket.h: #define SO_RECV_ANYIF 0x1104
recvanyif = unix.SetsockoptInt(int(fd), syscall.SOL_SOCKET, 0x1104, 1)
})
switch {
case reuseport != nil:
return reuseport
case recvanyif != nil:
return recvanyif
default:
return control
}
}

View file

@ -1,4 +1,4 @@
// +build linux darwin netbsd freebsd openbsd dragonflybsd
// +build linux netbsd freebsd openbsd dragonflybsd
package yggdrasil

View file

@ -227,6 +227,7 @@ func (p *peer) handlePacket(packet []byte) {
default:
util.PutBytes(packet)
}
return
}
// Called to handle traffic or protocolTraffic packets.

View file

@ -15,6 +15,7 @@ package yggdrasil
// See version.go for version metadata format
import (
"context"
"errors"
"fmt"
"io"
@ -120,7 +121,11 @@ func (iface *tcpInterface) listen() error {
iface.tcp_timeout = default_tcp_timeout
}
iface.serv, err = net.Listen("tcp", iface.tcp_addr)
ctx := context.Background()
lc := net.ListenConfig{
Control: iface.tcpContext,
}
iface.serv, err = lc.Listen(ctx, "tcp", iface.tcp_addr)
if err == nil {
iface.calls = make(map[string]struct{})
iface.conns = make(map[tcpInfo](chan struct{}))
@ -212,7 +217,9 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) {
},
}
} else {
dialer := net.Dialer{}
dialer := net.Dialer{
Control: iface.tcpContext,
}
if sintf != "" {
ief, err := net.InterfaceByName(sintf)
if err != nil {

View file

@ -0,0 +1,28 @@
// +build darwin
package yggdrasil
import (
"syscall"
"golang.org/x/sys/unix"
)
// WARNING: This context is used both by net.Dialer and net.Listen in tcp.go
func (iface *tcpInterface) tcpContext(network, address string, c syscall.RawConn) error {
var control error
var recvanyif error
control = c.Control(func(fd uintptr) {
// sys/socket.h: #define SO_RECV_ANYIF 0x1104
recvanyif = unix.SetsockoptInt(int(fd), syscall.SOL_SOCKET, 0x1104, 1)
})
switch {
case recvanyif != nil:
return recvanyif
default:
return control
}
}

View file

@ -0,0 +1,13 @@
// +build !darwin
package yggdrasil
import (
"syscall"
)
// WARNING: This context is used both by net.Dialer and net.Listen in tcp.go
func (iface *tcpInterface) tcpContext(network, address string, c syscall.RawConn) error {
return nil
}

View file

@ -47,11 +47,13 @@ func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte)
// Starts the setup process for the TUN/TAP adapter, and if successful, starts
// the read/write goroutines to handle packets on that interface.
func (tun *tunAdapter) start(ifname string, iftapmode bool, addr string, mtu int) error {
if ifname == "none" {
return nil
if ifname != "none" {
if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil {
return err
}
}
if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil {
return err
if ifname == "none" || ifname == "dummy" {
return nil
}
tun.mutex.Lock()
tun.isOpen = true

View file

@ -1,3 +1,5 @@
// +build !mobile
package yggdrasil
// The darwin platform specific tun parts

View file

@ -0,0 +1,19 @@
// +build mobile
package yggdrasil
// This is to catch unsupported platforms
// If your platform supports tun devices, you could try configuring it manually
// Creates the TUN/TAP adapter, if supported by the Water library. Note that
// no guarantees are made at this point on an unsupported platform.
func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
tun.mtu = getSupportedMTU(mtu)
return tun.setupAddress(addr)
}
// We don't know how to set the IPv6 address on an unknown platform, therefore
// write about it to stdout and don't try to do anything further.
func (tun *tunAdapter) setupAddress(addr string) error {
return nil
}

View file

@ -1,3 +1,5 @@
// +build !mobile
package yggdrasil
// The linux platform specific tun parts

View file

@ -1,4 +1,4 @@
// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd
// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd,!mobile
package yggdrasil