diff --git a/build b/build
index e463c852..7ceb4c9a 100755
--- a/build
+++ b/build
@@ -6,12 +6,14 @@ PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)}
LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER"
-while getopts "udtc:l:" option
+while getopts "udaitc:l:" option
do
case "${option}"
in
u) UPX=true;;
d) DEBUG=true;;
+ i) IOS=true;;
+ a) ANDROID=true;;
t) TABLES=true;;
c) GCFLAGS="$GCFLAGS $OPTARG";;
l) LDFLAGS="$LDFLAGS $OPTARG";;
@@ -22,15 +24,23 @@ if [ -z $TABLES ]; then
STRIP="-s -w"
fi
-for CMD in `ls cmd/` ; do
- echo "Building: $CMD"
+if [ $IOS ]; then
+ echo "Building framework for iOS"
+ gomobile bind -target ios -tags mobile -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil
+elif [ $ANDROID ]; then
+ echo "Building aar for Android"
+ gomobile bind -target android -tags mobile -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil
+else
+ for CMD in `ls cmd/` ; do
+ echo "Building: $CMD"
- if [ $DEBUG ]; then
- go build -ldflags="$LDFLAGS" -gcflags="$GCFLAGS" -tags debug -v ./cmd/$CMD
- else
- go build -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" -v ./cmd/$CMD
- fi
- if [ $UPX ]; then
- upx --brute $CMD
- fi
-done
+ if [ $DEBUG ]; then
+ go build -ldflags="$LDFLAGS" -gcflags="$GCFLAGS" -tags debug -v ./cmd/$CMD
+ else
+ go build -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" -v ./cmd/$CMD
+ fi
+ if [ $UPX ]; then
+ upx --brute $CMD
+ fi
+ done
+fi
diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go
index 5ee7add6..262fb682 100644
--- a/cmd/yggdrasil/main.go
+++ b/cmd/yggdrasil/main.go
@@ -2,13 +2,11 @@ package main
import (
"bytes"
- "encoding/hex"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
- "math/rand"
"os"
"os/signal"
"regexp"
@@ -23,7 +21,6 @@ import (
"github.com/mitchellh/mapstructure"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
- "github.com/yggdrasil-network/yggdrasil-go/src/defaults"
"github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
)
@@ -34,60 +31,18 @@ type node struct {
core Core
}
-// Generates default configuration. This is used when outputting the -genconf
-// parameter and also when using -autoconf. The isAutoconf flag is used to
-// determine whether the operating system should select a free port by itself
-// (which guarantees that there will not be a conflict with any other services)
-// or whether to generate a random port number. The only side effect of setting
-// isAutoconf is that the TCP and UDP ports will likely end up with different
-// port numbers.
-func generateConfig(isAutoconf bool) *nodeConfig {
- // Create a new core.
- core := Core{}
- // Generate encryption keys.
- bpub, bpriv := core.NewEncryptionKeys()
- spub, spriv := core.NewSigningKeys()
- // Create a node configuration and populate it.
- cfg := nodeConfig{}
- if isAutoconf {
- cfg.Listen = "[::]:0"
- } else {
- r1 := rand.New(rand.NewSource(time.Now().UnixNano()))
- cfg.Listen = fmt.Sprintf("[::]:%d", r1.Intn(65534-32768)+32768)
- }
- cfg.AdminListen = defaults.GetDefaults().DefaultAdminListen
- cfg.EncryptionPublicKey = hex.EncodeToString(bpub[:])
- cfg.EncryptionPrivateKey = hex.EncodeToString(bpriv[:])
- cfg.SigningPublicKey = hex.EncodeToString(spub[:])
- cfg.SigningPrivateKey = hex.EncodeToString(spriv[:])
- cfg.Peers = []string{}
- cfg.InterfacePeers = map[string][]string{}
- cfg.AllowedEncryptionPublicKeys = []string{}
- cfg.MulticastInterfaces = []string{".*"}
- cfg.IfName = defaults.GetDefaults().DefaultIfName
- cfg.IfMTU = defaults.GetDefaults().DefaultIfMTU
- cfg.IfTAPMode = defaults.GetDefaults().DefaultIfTAPMode
- cfg.SessionFirewall.Enable = false
- cfg.SessionFirewall.AllowFromDirect = true
- cfg.SessionFirewall.AllowFromRemote = true
- cfg.SwitchOptions.MaxTotalQueueSize = yggdrasil.SwitchQueueTotalMinSize
- cfg.NodeInfoPrivacy = false
-
- return &cfg
-}
-
func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeConfig {
// Use a configuration file. If -useconf, the configuration will be read
// from stdin. If -useconffile, the configuration will be read from the
// filesystem.
- var config []byte
+ var conf []byte
var err error
if *useconffile != "" {
// Read the file from the filesystem
- config, err = ioutil.ReadFile(*useconffile)
+ conf, err = ioutil.ReadFile(*useconffile)
} else {
// Read the file from stdin.
- config, err = ioutil.ReadAll(os.Stdin)
+ conf, err = ioutil.ReadAll(os.Stdin)
}
if err != nil {
panic(err)
@@ -96,11 +51,11 @@ func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeCo
// throwing everywhere when it's converting things into UTF-16 for the hell
// of it - remove it and decode back down into UTF-8. This is necessary
// because hjson doesn't know what to do with UTF-16 and will panic
- if bytes.Compare(config[0:2], []byte{0xFF, 0xFE}) == 0 ||
- bytes.Compare(config[0:2], []byte{0xFE, 0xFF}) == 0 {
+ if bytes.Compare(conf[0:2], []byte{0xFF, 0xFE}) == 0 ||
+ bytes.Compare(conf[0:2], []byte{0xFE, 0xFF}) == 0 {
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
decoder := utf.NewDecoder()
- config, err = decoder.Bytes(config)
+ conf, err = decoder.Bytes(conf)
if err != nil {
panic(err)
}
@@ -109,9 +64,9 @@ func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeCo
// then parse the configuration we loaded above on top of it. The effect
// of this is that any configuration item that is missing from the provided
// configuration will use a sane default.
- cfg := generateConfig(false)
+ cfg := config.GenerateConfig(false)
var dat map[string]interface{}
- if err := hjson.Unmarshal(config, &dat); err != nil {
+ if err := hjson.Unmarshal(conf, &dat); err != nil {
panic(err)
}
confJson, err := json.Marshal(dat)
@@ -192,7 +147,7 @@ func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeCo
// Generates a new configuration and returns it in HJSON format. This is used
// with -genconf.
func doGenconf(isjson bool) string {
- cfg := generateConfig(false)
+ cfg := config.GenerateConfig(false)
var bs []byte
var err error
if isjson {
@@ -228,7 +183,7 @@ func main() {
case *autoconf:
// Use an autoconf-generated config, this will give us random keys and
// port numbers, and will use an automatically selected TUN/TAP interface.
- cfg = generateConfig(true)
+ cfg = config.GenerateConfig(true)
case *useconffile != "" || *useconf:
// Read the configuration from either stdin or from the filesystem
cfg = readConfig(useconf, useconffile, normaliseconf)
diff --git a/contrib/logo/ygg-neilalexander.svg b/contrib/logo/ygg-neilalexander.svg
new file mode 100644
index 00000000..d2222002
--- /dev/null
+++ b/contrib/logo/ygg-neilalexander.svg
@@ -0,0 +1,157 @@
+
+
+
+
diff --git a/src/config/config.go b/src/config/config.go
index 192f435f..192b94e5 100644
--- a/src/config/config.go
+++ b/src/config/config.go
@@ -1,5 +1,15 @@
package config
+import (
+ "encoding/hex"
+ "fmt"
+ "math/rand"
+ "time"
+
+ "github.com/yggdrasil-network/yggdrasil-go/src/crypto"
+ "github.com/yggdrasil-network/yggdrasil-go/src/defaults"
+)
+
// NodeConfig defines all configuration values needed to run a signle yggdrasil node
type NodeConfig struct {
Listen string `comment:"Listen address for peer connections. Default is to listen for all\nTCP connections over IPv4 and IPv6 with a random port."`
@@ -53,3 +63,45 @@ type TunnelRouting struct {
type SwitchOptions struct {
MaxTotalQueueSize uint64 `comment:"Maximum size of all switch queues combined (in bytes)."`
}
+
+// Generates default configuration. This is used when outputting the -genconf
+// parameter and also when using -autoconf. The isAutoconf flag is used to
+// determine whether the operating system should select a free port by itself
+// (which guarantees that there will not be a conflict with any other services)
+// or whether to generate a random port number. The only side effect of setting
+// isAutoconf is that the TCP and UDP ports will likely end up with different
+// port numbers.
+func GenerateConfig(isAutoconf bool) *NodeConfig {
+ // Create a new core.
+ //core := Core{}
+ // Generate encryption keys.
+ bpub, bpriv := crypto.NewBoxKeys()
+ spub, spriv := crypto.NewSigKeys()
+ // Create a node configuration and populate it.
+ cfg := NodeConfig{}
+ if isAutoconf {
+ cfg.Listen = "[::]:0"
+ } else {
+ r1 := rand.New(rand.NewSource(time.Now().UnixNano()))
+ cfg.Listen = fmt.Sprintf("[::]:%d", r1.Intn(65534-32768)+32768)
+ }
+ cfg.AdminListen = defaults.GetDefaults().DefaultAdminListen
+ cfg.EncryptionPublicKey = hex.EncodeToString(bpub[:])
+ cfg.EncryptionPrivateKey = hex.EncodeToString(bpriv[:])
+ cfg.SigningPublicKey = hex.EncodeToString(spub[:])
+ cfg.SigningPrivateKey = hex.EncodeToString(spriv[:])
+ cfg.Peers = []string{}
+ cfg.InterfacePeers = map[string][]string{}
+ cfg.AllowedEncryptionPublicKeys = []string{}
+ cfg.MulticastInterfaces = []string{".*"}
+ cfg.IfName = defaults.GetDefaults().DefaultIfName
+ cfg.IfMTU = defaults.GetDefaults().DefaultIfMTU
+ cfg.IfTAPMode = defaults.GetDefaults().DefaultIfTAPMode
+ cfg.SessionFirewall.Enable = false
+ cfg.SessionFirewall.AllowFromDirect = true
+ cfg.SessionFirewall.AllowFromRemote = true
+ cfg.SwitchOptions.MaxTotalQueueSize = 4 * 1024 * 1024
+ cfg.NodeInfoPrivacy = false
+
+ return &cfg
+}
diff --git a/src/yggdrasil/adapter.go b/src/yggdrasil/adapter.go
index 4a432092..7fb6a19e 100644
--- a/src/yggdrasil/adapter.go
+++ b/src/yggdrasil/adapter.go
@@ -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
diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go
new file mode 100644
index 00000000..633d5f9c
--- /dev/null
+++ b/src/yggdrasil/awdl.go
@@ -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
+ }
+ }
+}
diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go
index d4a22680..7e10dbca 100644
--- a/src/yggdrasil/core.go
+++ b/src/yggdrasil/core.go
@@ -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
}
diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go
new file mode 100644
index 00000000..2ffeffb9
--- /dev/null
+++ b/src/yggdrasil/mobile.go
@@ -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
+}
diff --git a/src/yggdrasil/mobile_android.go b/src/yggdrasil/mobile_android.go
new file mode 100644
index 00000000..24764840
--- /dev/null
+++ b/src/yggdrasil/mobile_android.go
@@ -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
+}
diff --git a/src/yggdrasil/mobile_ios.go b/src/yggdrasil/mobile_ios.go
new file mode 100644
index 00000000..72920fe0
--- /dev/null
+++ b/src/yggdrasil/mobile_ios.go
@@ -0,0 +1,68 @@
+// +build mobile,darwin
+
+package yggdrasil
+
+/*
+#cgo CFLAGS: -x objective-c
+#cgo LDFLAGS: -framework Foundation
+#import
+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)
+}
diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go
index 218f5162..cf84fed6 100644
--- a/src/yggdrasil/multicast.go
+++ b/src/yggdrasil/multicast.go
@@ -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)
}
}
diff --git a/src/yggdrasil/multicast_darwin.go b/src/yggdrasil/multicast_darwin.go
new file mode 100644
index 00000000..71eecce4
--- /dev/null
+++ b/src/yggdrasil/multicast_darwin.go
@@ -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
+ }
+}
diff --git a/src/yggdrasil/multicast_unix.go b/src/yggdrasil/multicast_unix.go
index 9c6d1f11..54bbc645 100644
--- a/src/yggdrasil/multicast_unix.go
+++ b/src/yggdrasil/multicast_unix.go
@@ -1,4 +1,4 @@
-// +build linux darwin netbsd freebsd openbsd dragonflybsd
+// +build linux netbsd freebsd openbsd dragonflybsd
package yggdrasil
diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go
index 15174b77..aaa6a88a 100644
--- a/src/yggdrasil/peer.go
+++ b/src/yggdrasil/peer.go
@@ -227,6 +227,7 @@ func (p *peer) handlePacket(packet []byte) {
default:
util.PutBytes(packet)
}
+ return
}
// Called to handle traffic or protocolTraffic packets.
diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go
index ec8bca46..b47b5531 100644
--- a/src/yggdrasil/tcp.go
+++ b/src/yggdrasil/tcp.go
@@ -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 {
diff --git a/src/yggdrasil/tcp_darwin.go b/src/yggdrasil/tcp_darwin.go
new file mode 100644
index 00000000..6483ef86
--- /dev/null
+++ b/src/yggdrasil/tcp_darwin.go
@@ -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
+ }
+}
diff --git a/src/yggdrasil/tcp_other.go b/src/yggdrasil/tcp_other.go
new file mode 100644
index 00000000..5d62b530
--- /dev/null
+++ b/src/yggdrasil/tcp_other.go
@@ -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
+}
diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go
index 8ed53332..8c0f91d5 100644
--- a/src/yggdrasil/tun.go
+++ b/src/yggdrasil/tun.go
@@ -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
diff --git a/src/yggdrasil/tun_darwin.go b/src/yggdrasil/tun_darwin.go
index 943468e6..828c01ea 100644
--- a/src/yggdrasil/tun_darwin.go
+++ b/src/yggdrasil/tun_darwin.go
@@ -1,3 +1,5 @@
+// +build !mobile
+
package yggdrasil
// The darwin platform specific tun parts
diff --git a/src/yggdrasil/tun_dummy.go b/src/yggdrasil/tun_dummy.go
new file mode 100644
index 00000000..234ab1de
--- /dev/null
+++ b/src/yggdrasil/tun_dummy.go
@@ -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
+}
diff --git a/src/yggdrasil/tun_linux.go b/src/yggdrasil/tun_linux.go
index 7a7c9cb7..8ccdd30b 100644
--- a/src/yggdrasil/tun_linux.go
+++ b/src/yggdrasil/tun_linux.go
@@ -1,3 +1,5 @@
+// +build !mobile
+
package yggdrasil
// The linux platform specific tun parts
diff --git a/src/yggdrasil/tun_other.go b/src/yggdrasil/tun_other.go
index 625f9cd5..22058c11 100644
--- a/src/yggdrasil/tun_other.go
+++ b/src/yggdrasil/tun_other.go
@@ -1,4 +1,4 @@
-// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd
+// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd,!mobile
package yggdrasil