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 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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