diff --git a/build b/build index bad287f..383b91a 100755 --- a/build +++ b/build @@ -31,14 +31,14 @@ 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 \ - github.com/yggdrasil-network/yggdrasil-go/src/mobile \ - github.com/yggdrasil-network/yggdrasil-go/src/config + github.com/yggdrasil-network/yggdrasil-go/src/config \ + github.com/yggdrasil-network/yggdrasil-extras/src/mobile 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 \ - github.com/yggdrasil-network/yggdrasil-go/src/mobile \ - github.com/yggdrasil-network/yggdrasil-go/src/config + github.com/yggdrasil-network/yggdrasil-go/src/config \ + github.com/yggdrasil-network/yggdrasil-extras/src/mobile else for CMD in `ls cmd/` ; do echo "Building: $CMD" diff --git a/src/admin/admin.go b/src/admin/admin.go index fc8a6a5..2b73764 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -60,7 +60,7 @@ func (a *AdminSocket) Init(c *yggdrasil.Core, state *config.NodeState, log *log. go func() { for { e := <-a.reconfigure - current, previous := state.Get() + current, previous := state.GetCurrent(), state.GetPrevious() if current.AdminListen != previous.AdminListen { a.listenaddr = current.AdminListen a.Stop() @@ -69,7 +69,7 @@ func (a *AdminSocket) Init(c *yggdrasil.Core, state *config.NodeState, log *log. e <- nil } }() - current, _ := state.Get() + current := state.GetCurrent() a.listenaddr = current.AdminListen a.AddHandler("list", []string{}, func(in Info) (Info, error) { handlers := make(map[string]interface{}) @@ -85,14 +85,15 @@ func (a *AdminSocket) Init(c *yggdrasil.Core, state *config.NodeState, log *log. */ a.AddHandler("getSelf", []string{}, func(in Info) (Info, error) { ip := c.Address().String() + subnet := c.Subnet() return Info{ "self": Info{ ip: Info{ - "box_pub_key": c.BoxPubKey(), + "box_pub_key": c.EncryptionPublicKey(), "build_name": yggdrasil.BuildName(), "build_version": yggdrasil.BuildVersion(), "coords": fmt.Sprintf("%v", c.Coords()), - "subnet": c.Subnet().String(), + "subnet": subnet.String(), }, }, }, nil diff --git a/src/config/config.go b/src/config/config.go index 9f8f3f5..a7bbbac 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -16,21 +16,27 @@ type NodeState struct { Mutex sync.RWMutex } -// Get returns both the current and previous node configs -func (s *NodeState) Get() (NodeConfig, NodeConfig) { +// Current returns the current node config +func (s *NodeState) GetCurrent() NodeConfig { s.Mutex.RLock() defer s.Mutex.RUnlock() - return s.Current, s.Previous + return s.Current +} + +// Previous returns the previous node config +func (s *NodeState) GetPrevious() NodeConfig { + s.Mutex.RLock() + defer s.Mutex.RUnlock() + return s.Previous } // Replace the node configuration with new configuration. This method returns // both the new and the previous node configs -func (s *NodeState) Replace(n NodeConfig) (NodeConfig, NodeConfig) { +func (s *NodeState) Replace(n NodeConfig) { s.Mutex.Lock() defer s.Mutex.Unlock() s.Previous = s.Current s.Current = n - return s.Current, s.Previous } // NodeConfig defines all configuration values needed to run a signle yggdrasil node @@ -115,3 +121,19 @@ func GenerateConfig() *NodeConfig { return &cfg } + +// NewEncryptionKeys generates a new encryption keypair. The encryption keys are +// used to encrypt traffic and to derive the IPv6 address/subnet of the node. +func (cfg *NodeConfig) NewEncryptionKeys() { + bpub, bpriv := crypto.NewBoxKeys() + cfg.EncryptionPublicKey = hex.EncodeToString(bpub[:]) + cfg.EncryptionPrivateKey = hex.EncodeToString(bpriv[:]) +} + +// NewSigningKeys generates a new signing keypair. The signing keys are used to +// derive the structure of the spanning tree. +func (cfg *NodeConfig) NewSigningKeys() { + spub, spriv := crypto.NewSigKeys() + cfg.SigningPublicKey = hex.EncodeToString(spub[:]) + cfg.SigningPrivateKey = hex.EncodeToString(spriv[:]) +} diff --git a/src/dummy/dummy.go b/src/dummy/dummy.go deleted file mode 100644 index ca6bb7b..0000000 --- a/src/dummy/dummy.go +++ /dev/null @@ -1,61 +0,0 @@ -package dummy - -import ( - "github.com/gologme/log" - "github.com/yggdrasil-network/yggdrasil-go/src/address" - "github.com/yggdrasil-network/yggdrasil-go/src/config" - "github.com/yggdrasil-network/yggdrasil-go/src/defaults" - "github.com/yggdrasil-network/yggdrasil-go/src/util" - "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" -) - -// DummyAdapter is a non-specific adapter that is used by the mobile APIs. -// You can also use it to send or receive custom traffic over Yggdrasil. -type DummyAdapter struct { - yggdrasil.Adapter -} - -// Init initialises the dummy adapter. -func (m *DummyAdapter) Init(config *config.NodeState, log *log.Logger, send chan<- []byte, recv <-chan []byte, reject <-chan yggdrasil.RejectedPacket) { - m.Adapter.Init(config, log, send, recv, reject) -} - -// Name returns the name of the adapter. This is always "dummy" for dummy -// adapters. -func (m *DummyAdapter) Name() string { - return "dummy" -} - -// MTU gets the adapter's MTU. This returns your platform's maximum MTU for -// dummy adapters. -func (m *DummyAdapter) MTU() int { - return defaults.GetDefaults().MaximumIfMTU -} - -// IsTAP always returns false for dummy adapters. -func (m *DummyAdapter) IsTAP() bool { - return false -} - -// Recv waits for and returns for a packet from the router. -func (m *DummyAdapter) Recv() ([]byte, error) { - packet := <-m.Adapter.Recv - return packet, nil -} - -// Send a packet to the router. -func (m *DummyAdapter) Send(buf []byte) error { - packet := append(util.GetBytes(), buf[:]...) - m.Adapter.Send <- packet - return nil -} - -// Start is not implemented for dummy adapters. -func (m *DummyAdapter) Start(address.Address, address.Subnet) error { - return nil -} - -// Close is not implemented for dummy adapters. -func (m *DummyAdapter) Close() error { - return nil -} diff --git a/src/mobile/awdl.go b/src/mobile/awdl.go deleted file mode 100644 index 9a073ef..0000000 --- a/src/mobile/awdl.go +++ /dev/null @@ -1,108 +0,0 @@ -package mobile - -/* -import ( - "errors" - "io" - "sync" -) - -type awdl struct { - link *link - reconfigure chan chan error - mutex sync.RWMutex // protects interfaces below - interfaces map[string]*awdlInterface -} - -type awdlInterface struct { - linkif *linkInterface - rwc awdlReadWriteCloser - peer *peer - stream stream -} - -type awdlReadWriteCloser struct { - fromAWDL chan []byte - toAWDL chan []byte -} - -func (c awdlReadWriteCloser) Read(p []byte) (n int, err error) { - if packet, ok := <-c.fromAWDL; ok { - n = copy(p, packet) - return n, nil - } - return 0, io.EOF -} - -func (c awdlReadWriteCloser) Write(p []byte) (n int, err error) { - var pc []byte - pc = append(pc, p...) - c.toAWDL <- pc - return len(pc), nil -} - -func (c awdlReadWriteCloser) Close() error { - close(c.fromAWDL) - close(c.toAWDL) - return nil -} - -func (a *awdl) init(l *link) error { - a.link = l - a.mutex.Lock() - a.interfaces = make(map[string]*awdlInterface) - a.reconfigure = make(chan chan error, 1) - a.mutex.Unlock() - - go func() { - for e := range a.reconfigure { - e <- nil - } - }() - - return nil -} - -func (a *awdl) create(name, local, remote string, incoming bool) (*awdlInterface, error) { - rwc := awdlReadWriteCloser{ - fromAWDL: make(chan []byte, 1), - toAWDL: make(chan []byte, 1), - } - s := stream{} - s.init(rwc) - linkif, err := a.link.create(&s, name, "awdl", local, remote, incoming, true) - if err != nil { - return nil, err - } - intf := awdlInterface{ - linkif: linkif, - rwc: rwc, - } - a.mutex.Lock() - a.interfaces[name] = &intf - a.mutex.Unlock() - go intf.linkif.handler() - return &intf, nil -} - -func (a *awdl) getInterface(identity string) *awdlInterface { - a.mutex.RLock() - defer a.mutex.RUnlock() - if intf, ok := a.interfaces[identity]; ok { - return intf - } - return nil -} - -func (a *awdl) shutdown(identity string) error { - if intf, ok := a.interfaces[identity]; ok { - close(intf.linkif.closed) - intf.rwc.Close() - a.mutex.Lock() - delete(a.interfaces, identity) - a.mutex.Unlock() - return nil - } - return errors.New("Interface not found or already closed") -} -*/ diff --git a/src/mobile/mobile.go b/src/mobile/mobile.go deleted file mode 100644 index 02bd30f..0000000 --- a/src/mobile/mobile.go +++ /dev/null @@ -1,149 +0,0 @@ -package mobile - -import ( - "encoding/json" - "os" - "time" - - "github.com/gologme/log" - - 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/dummy" - "github.com/yggdrasil-network/yggdrasil-go/src/multicast" - "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" -) - -// Yggdrasil mobile package 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. -type Yggdrasil struct { - core yggdrasil.Core - multicast multicast.Multicast - log MobileLogger - dummy.DummyAdapter -} - -func (m *Yggdrasil) addStaticPeers(cfg *config.NodeConfig) { - if len(cfg.Peers) == 0 && len(cfg.InterfacePeers) == 0 { - return - } - for { - for _, peer := range cfg.Peers { - m.core.AddPeer(peer, "") - time.Sleep(time.Second) - } - for intf, intfpeers := range cfg.InterfacePeers { - for _, peer := range intfpeers { - m.core.AddPeer(peer, intf) - time.Sleep(time.Second) - } - } - time.Sleep(time.Minute) - } -} - -// StartAutoconfigure starts a node with a randomly generated config -func (m *Yggdrasil) StartAutoconfigure() error { - logger := log.New(m.log, "", 0) - logger.EnableLevel("error") - logger.EnableLevel("warn") - logger.EnableLevel("info") - nc := config.GenerateConfig() - 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} - } - if err := m.core.SetRouterAdapter(m); err != nil { - logger.Errorln("An error occured setting router adapter:", err) - return err - } - state, err := m.core.Start(nc, logger) - if err != nil { - logger.Errorln("An error occured starting Yggdrasil:", err) - return err - } - m.multicast.Init(&m.core, state, logger, nil) - if err := m.multicast.Start(); err != nil { - logger.Errorln("An error occurred starting multicast:", err) - } - go m.addStaticPeers(nc) - return nil -} - -// StartJSON starts a node with the given JSON config. You can get JSON config -// (rather than HJSON) by using the GenerateConfigJSON() function -func (m *Yggdrasil) StartJSON(configjson []byte) error { - logger := log.New(m.log, "", 0) - logger.EnableLevel("error") - logger.EnableLevel("warn") - logger.EnableLevel("info") - nc := config.GenerateConfig() - 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" - if err := m.core.SetRouterAdapter(m); err != nil { - logger.Errorln("An error occured setting router adapter:", err) - return err - } - state, err := m.core.Start(nc, logger) - if err != nil { - logger.Errorln("An error occured starting Yggdrasil:", err) - return err - } - m.multicast.Init(&m.core, state, logger, nil) - if err := m.multicast.Start(); err != nil { - logger.Errorln("An error occurred starting multicast:", err) - } - go m.addStaticPeers(nc) - return nil -} - -// Stop the mobile Yggdrasil instance -func (m *Yggdrasil) Stop() error { - m.core.Stop() - if err := m.Stop(); err != nil { - return err - } - return nil -} - -// GenerateConfigJSON generates mobile-friendly configuration in JSON format -func GenerateConfigJSON() []byte { - nc := config.GenerateConfig() - nc.IfName = "dummy" - if json, err := json.Marshal(nc); err == nil { - return json - } - return nil -} - -// GetAddressString gets the node's IPv6 address -func (m *Yggdrasil) GetAddressString() string { - return m.core.Address().String() -} - -// GetSubnetString gets the node's IPv6 subnet in CIDR notation -func (m *Yggdrasil) GetSubnetString() string { - return m.core.Subnet().String() -} - -// GetBoxPubKeyString gets the node's public encryption key -func (m *Yggdrasil) GetBoxPubKeyString() string { - return m.core.BoxPubKey() -} - -// GetSigPubKeyString gets the node's public signing key -func (m *Yggdrasil) GetSigPubKeyString() string { - return m.core.SigPubKey() -} diff --git a/src/mobile/mobile_android.go b/src/mobile/mobile_android.go deleted file mode 100644 index f3206ac..0000000 --- a/src/mobile/mobile_android.go +++ /dev/null @@ -1,12 +0,0 @@ -// +build android - -package mobile - -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/mobile/mobile_ios.go b/src/mobile/mobile_ios.go deleted file mode 100644 index 26b219c..0000000 --- a/src/mobile/mobile_ios.go +++ /dev/null @@ -1,61 +0,0 @@ -// +build darwin - -package mobile - -/* -#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 ( - "unsafe" -) - -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, local, remote string, incoming bool) error { - if intf, err := c.link.awdl.create(name, local, remote, incoming); err != nil || intf == nil { - c.log.Println("c.link.awdl.create:", err) - return err - } - return nil -} - -func (c *Core) AWDLShutdownInterface(name string) error { - return c.link.awdl.shutdown(name) -} - -func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) { - if intf := c.link.awdl.getInterface(identity); intf != nil { - read, ok := <-intf.rwc.toAWDL - if !ok { - return nil, errors.New("AWDLRecvPacket: channel closed") - } - return read, 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.link.awdl.getInterface(identity); intf != nil { - intf.rwc.fromAWDL <- packet - return nil - } - return errors.New("AWDLSendPacket identity not known: " + identity) -} -*/ diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index ba1f18f..5ab9673 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -35,7 +35,7 @@ func (m *Multicast) Init(core *yggdrasil.Core, state *config.NodeState, log *log m.config = state m.log = log m.listeners = make(map[string]*yggdrasil.TcpListener) - current, _ := m.config.Get() + current := m.config.GetCurrent() m.listenPort = current.LinkLocalTCPPort m.groupAddr = "[ff02::114]:9001" return nil @@ -92,7 +92,7 @@ func (m *Multicast) UpdateConfig(config *config.NodeConfig) { func (m *Multicast) Interfaces() map[string]net.Interface { interfaces := make(map[string]net.Interface) // Get interface expressions from config - current, _ := m.config.Get() + current := m.config.GetCurrent() exprs := current.MulticastInterfaces // Ask the system for network interfaces allifaces, err := net.Interfaces() diff --git a/src/tuntap/ckr.go b/src/tuntap/ckr.go index 52c1159..80e3e42 100644 --- a/src/tuntap/ckr.go +++ b/src/tuntap/ckr.go @@ -59,7 +59,7 @@ func (c *cryptokey) init(tun *TunAdapter) { // Configure the CKR routes - this must only ever be called from the router // goroutine, e.g. through router.doAdmin func (c *cryptokey) configure() error { - current, _ := c.tun.config.Get() + current := c.tun.config.GetCurrent() // Set enabled/disabled state c.setEnabled(current.TunnelRouting.Enable) diff --git a/src/tuntap/conn.go b/src/tuntap/conn.go index 193c623..1d47b37 100644 --- a/src/tuntap/conn.go +++ b/src/tuntap/conn.go @@ -108,10 +108,10 @@ func (s *tunConn) writer() error { } else { s.tun.log.Errorln(s.conn.String(), "TUN/TAP generic write error:", err) } - } else if ispackettoobig, maxsize := e.PacketTooBig(); ispackettoobig { + } else if e.PacketTooBig() { // TODO: This currently isn't aware of IPv4 for CKR ptb := &icmp.PacketTooBig{ - MTU: int(maxsize), + MTU: int(e.PacketMaximumSize()), Data: b[:900], } if packet, err := CreateICMPv6(b[8:24], b[24:40], ipv6.ICMPTypePacketTooBig, 0, ptb); err == nil { diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index cc12497..eef05b8 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -119,7 +119,7 @@ func (tun *TunAdapter) Init(config *config.NodeState, log *log.Logger, listener // Start the setup process for the TUN/TAP adapter. If successful, starts the // read/write goroutines to handle packets on that interface. func (tun *TunAdapter) Start() error { - current, _ := tun.config.Get() + current := tun.config.GetCurrent() if tun.config == nil || tun.listener == nil || tun.dialer == nil { return errors.New("No configuration available to TUN/TAP") } diff --git a/src/tuntap/tun_dummy.go b/src/tuntap/tun_dummy.go deleted file mode 100644 index 04e6525..0000000 --- a/src/tuntap/tun_dummy.go +++ /dev/null @@ -1,19 +0,0 @@ -// +build mobile - -package tuntap - -// 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/api.go b/src/yggdrasil/api.go index 1bec983..bf64324 100644 --- a/src/yggdrasil/api.go +++ b/src/yggdrasil/api.go @@ -59,7 +59,7 @@ type DHTRes struct { } // NodeInfoPayload represents a RequestNodeInfo response, in bytes. -type NodeInfoPayload nodeinfoPayload +type NodeInfoPayload []byte // SwitchQueues represents information from the switch related to link // congestion and a list of switch queues created in response to congestion on a @@ -261,7 +261,7 @@ func BuildVersion() string { return buildVersion } -// ListenConn returns a listener for Yggdrasil session connections. +// ConnListen returns a listener for Yggdrasil session connections. func (c *Core) ConnListen() (*Listener, error) { c.sessions.listenerMutex.Lock() defer c.sessions.listenerMutex.Unlock() @@ -290,18 +290,6 @@ func (c *Core) ListenTCP(uri string) (*TcpListener, error) { return c.link.tcp.listen(uri) } -// NewEncryptionKeys generates a new encryption keypair. The encryption keys are -// used to encrypt traffic and to derive the IPv6 address/subnet of the node. -func (c *Core) NewEncryptionKeys() (*crypto.BoxPubKey, *crypto.BoxPrivKey) { - return crypto.NewBoxKeys() -} - -// NewSigningKeys generates a new signing keypair. The signing keys are used to -// derive the structure of the spanning tree. -func (c *Core) NewSigningKeys() (*crypto.SigPubKey, *crypto.SigPrivKey) { - return crypto.NewSigKeys() -} - // NodeID gets the node ID. func (c *Core) NodeID() *crypto.NodeID { return crypto.GetNodeID(&c.boxPub) @@ -312,13 +300,13 @@ func (c *Core) TreeID() *crypto.TreeID { return crypto.GetTreeID(&c.sigPub) } -// SigPubKey gets the node's signing public key. -func (c *Core) SigPubKey() string { +// SigningPublicKey gets the node's signing public key. +func (c *Core) SigningPublicKey() string { return hex.EncodeToString(c.sigPub[:]) } -// BoxPubKey gets the node's encryption public key. -func (c *Core) BoxPubKey() string { +// EncryptionPublicKey gets the node's encryption public key. +func (c *Core) EncryptionPublicKey() string { return hex.EncodeToString(c.boxPub[:]) } @@ -330,27 +318,21 @@ func (c *Core) Coords() []byte { // Address gets the IPv6 address of the Yggdrasil node. This is always a /128 // address. -func (c *Core) Address() *net.IP { +func (c *Core) Address() net.IP { address := net.IP(address.AddrForNodeID(c.NodeID())[:]) - return &address + return address } // Subnet gets the routed IPv6 subnet of the Yggdrasil node. This is always a // /64 subnet. -func (c *Core) Subnet() *net.IPNet { +func (c *Core) Subnet() net.IPNet { subnet := address.SubnetForNodeID(c.NodeID())[:] subnet = append(subnet, 0, 0, 0, 0, 0, 0, 0, 0) - return &net.IPNet{IP: subnet, Mask: net.CIDRMask(64, 128)} + return net.IPNet{IP: subnet, Mask: net.CIDRMask(64, 128)} } -// RouterAddresses returns the raw address and subnet types as used by the -// router -func (c *Core) RouterAddresses() (address.Address, address.Subnet) { - return c.router.addr, c.router.subnet -} - -// NodeInfo gets the currently configured nodeinfo. -func (c *Core) MyNodeInfo() nodeinfoPayload { +// MyNodeInfo gets the currently configured nodeinfo. +func (c *Core) MyNodeInfo() NodeInfoPayload { return c.router.nodeinfo.getNodeInfo() } @@ -373,7 +355,7 @@ func (c *Core) GetNodeInfo(keyString, coordString string, nocache bool) (NodeInf } if !nocache { if response, err := c.router.nodeinfo.getCachedNodeInfo(key); err == nil { - return NodeInfoPayload(response), nil + return response, nil } } var coords []byte @@ -388,9 +370,9 @@ func (c *Core) GetNodeInfo(keyString, coordString string, nocache bool) (NodeInf coords = append(coords, uint8(u64)) } } - response := make(chan *nodeinfoPayload, 1) + response := make(chan *NodeInfoPayload, 1) sendNodeInfoRequest := func() { - c.router.nodeinfo.addCallback(key, func(nodeinfo *nodeinfoPayload) { + c.router.nodeinfo.addCallback(key, func(nodeinfo *NodeInfoPayload) { defer func() { recover() }() select { case response <- nodeinfo: @@ -405,9 +387,9 @@ func (c *Core) GetNodeInfo(keyString, coordString string, nocache bool) (NodeInf close(response) }() for res := range response { - return NodeInfoPayload(*res), nil + return *res, nil } - return NodeInfoPayload{}, errors.New(fmt.Sprintf("getNodeInfo timeout: %s", keyString)) + return NodeInfoPayload{}, fmt.Errorf("getNodeInfo timeout: %s", keyString) } // SetSessionGatekeeper allows you to configure a handler function for deciding @@ -493,7 +475,8 @@ func (c *Core) RemoveAllowedEncryptionPublicKey(bstr string) (err error) { return nil } -// Send a DHT ping to the node with the provided key and coords, optionally looking up the specified target NodeID. +// DHTPing sends a DHT ping to the node with the provided key and coords, +// optionally looking up the specified target NodeID. func (c *Core) DHTPing(keyString, coordString, targetString string) (DHTRes, error) { var key crypto.BoxPubKey if keyBytes, err := hex.DecodeString(keyString); err != nil { @@ -553,5 +536,5 @@ func (c *Core) DHTPing(keyString, coordString, targetString string) (DHTRes, err } return r, nil } - return DHTRes{}, errors.New(fmt.Sprintf("DHT ping timeout: %s", keyString)) + return DHTRes{}, fmt.Errorf("DHT ping timeout: %s", keyString) } diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index bc884fb..cd61e24 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -35,8 +35,17 @@ func (e *ConnError) Temporary() bool { // PacketTooBig returns in response to sending a packet that is too large, and // if so, the maximum supported packet size that should be used for the // connection. -func (e *ConnError) PacketTooBig() (bool, int) { - return e.maxsize > 0, e.maxsize +func (e *ConnError) PacketTooBig() bool { + return e.maxsize > 0 +} + +// PacketMaximumSize returns the maximum supported packet size. This will only +// return a non-zero value if ConnError.PacketTooBig() returns true. +func (e *ConnError) PacketMaximumSize() int { + if !e.PacketTooBig() { + return 0 + } + return e.maxsize } // Closed returns if the session is already closed and is now unusable. diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 35a86df..2aa7834 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -45,7 +45,7 @@ func (c *Core) init() error { c.log = log.New(ioutil.Discard, "", 0) } - current, _ := c.config.Get() + current := c.config.GetCurrent() boxPrivHex, err := hex.DecodeString(current.EncryptionPrivateKey) if err != nil { @@ -94,7 +94,7 @@ func (c *Core) init() error { func (c *Core) addPeerLoop() { for { // the peers from the config - these could change! - current, _ := c.config.Get() + current := c.config.GetCurrent() // Add peers from the Peers section for _, peer := range current.Peers { diff --git a/src/yggdrasil/nodeinfo.go b/src/yggdrasil/nodeinfo.go index 89b8b89..f1c7ed0 100644 --- a/src/yggdrasil/nodeinfo.go +++ b/src/yggdrasil/nodeinfo.go @@ -13,7 +13,7 @@ import ( type nodeinfo struct { core *Core - myNodeInfo nodeinfoPayload + myNodeInfo NodeInfoPayload myNodeInfoMutex sync.RWMutex callbacks map[crypto.BoxPubKey]nodeinfoCallback callbacksMutex sync.Mutex @@ -21,15 +21,13 @@ type nodeinfo struct { cacheMutex sync.RWMutex } -type nodeinfoPayload []byte - type nodeinfoCached struct { - payload nodeinfoPayload + payload NodeInfoPayload created time.Time } type nodeinfoCallback struct { - call func(nodeinfo *nodeinfoPayload) + call func(nodeinfo *NodeInfoPayload) created time.Time } @@ -38,7 +36,7 @@ type nodeinfoReqRes struct { SendPermPub crypto.BoxPubKey // Sender's permanent key SendCoords []byte // Sender's coords IsResponse bool - NodeInfo nodeinfoPayload + NodeInfo NodeInfoPayload } // Initialises the nodeinfo cache/callback maps, and starts a goroutine to keep @@ -70,7 +68,7 @@ func (m *nodeinfo) init(core *Core) { } // Add a callback for a nodeinfo lookup -func (m *nodeinfo) addCallback(sender crypto.BoxPubKey, call func(nodeinfo *nodeinfoPayload)) { +func (m *nodeinfo) addCallback(sender crypto.BoxPubKey, call func(nodeinfo *NodeInfoPayload)) { m.callbacksMutex.Lock() defer m.callbacksMutex.Unlock() m.callbacks[sender] = nodeinfoCallback{ @@ -80,7 +78,7 @@ func (m *nodeinfo) addCallback(sender crypto.BoxPubKey, call func(nodeinfo *node } // Handles the callback, if there is one -func (m *nodeinfo) callback(sender crypto.BoxPubKey, nodeinfo nodeinfoPayload) { +func (m *nodeinfo) callback(sender crypto.BoxPubKey, nodeinfo NodeInfoPayload) { m.callbacksMutex.Lock() defer m.callbacksMutex.Unlock() if callback, ok := m.callbacks[sender]; ok { @@ -90,7 +88,7 @@ func (m *nodeinfo) callback(sender crypto.BoxPubKey, nodeinfo nodeinfoPayload) { } // Get the current node's nodeinfo -func (m *nodeinfo) getNodeInfo() nodeinfoPayload { +func (m *nodeinfo) getNodeInfo() NodeInfoPayload { m.myNodeInfoMutex.RLock() defer m.myNodeInfoMutex.RUnlock() return m.myNodeInfo @@ -135,7 +133,7 @@ func (m *nodeinfo) setNodeInfo(given interface{}, privacy bool) error { } // Add nodeinfo into the cache for a node -func (m *nodeinfo) addCachedNodeInfo(key crypto.BoxPubKey, payload nodeinfoPayload) { +func (m *nodeinfo) addCachedNodeInfo(key crypto.BoxPubKey, payload NodeInfoPayload) { m.cacheMutex.Lock() defer m.cacheMutex.Unlock() m.cache[key] = nodeinfoCached{ @@ -145,13 +143,13 @@ func (m *nodeinfo) addCachedNodeInfo(key crypto.BoxPubKey, payload nodeinfoPaylo } // Get a nodeinfo entry from the cache -func (m *nodeinfo) getCachedNodeInfo(key crypto.BoxPubKey) (nodeinfoPayload, error) { +func (m *nodeinfo) getCachedNodeInfo(key crypto.BoxPubKey) (NodeInfoPayload, error) { m.cacheMutex.RLock() defer m.cacheMutex.RUnlock() if nodeinfo, ok := m.cache[key]; ok { return nodeinfo.payload, nil } - return nodeinfoPayload{}, errors.New("No cache entry found") + return NodeInfoPayload{}, errors.New("No cache entry found") } // Handles a nodeinfo request/response - called from the router diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 514d14f..c5e1dde 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -132,187 +132,12 @@ func (r *router) mainLoop() { case f := <-r.admin: f() case e := <-r.reconfigure: - current, _ := r.core.config.Get() + current := r.core.config.GetCurrent() e <- r.nodeinfo.setNodeInfo(current.NodeInfo, current.NodeInfoPrivacy) } } } -/* -// Checks a packet's to/from address to make sure it's in the allowed range. -// If a session to the destination exists, gets the session and passes the packet to it. -// If no session exists, it triggers (or continues) a search. -// If the session hasn't responded recently, it triggers a ping or search to keep things alive or deal with broken coords *relatively* quickly. -// It also deals with oversized packets if there are MTU issues by calling into icmpv6.go to spoof PacketTooBig traffic, or DestinationUnreachable if the other side has their adapter disabled. -func (r *router) sendPacket(bs []byte) { - var sourceAddr address.Address - var destAddr address.Address - var destSnet address.Subnet - var destPubKey *crypto.BoxPubKey - var destNodeID *crypto.NodeID - var addrlen int - if bs[0]&0xf0 == 0x60 { - // Check if we have a fully-sized header - if len(bs) < 40 { - panic("Tried to send a packet shorter than an IPv6 header...") - } - // IPv6 address - addrlen = 16 - copy(sourceAddr[:addrlen], bs[8:]) - copy(destAddr[:addrlen], bs[24:]) - copy(destSnet[:addrlen/2], bs[24:]) - } else if bs[0]&0xf0 == 0x40 { - // Check if we have a fully-sized header - if len(bs) < 20 { - panic("Tried to send a packet shorter than an IPv4 header...") - } - // IPv4 address - addrlen = 4 - copy(sourceAddr[:addrlen], bs[12:]) - copy(destAddr[:addrlen], bs[16:]) - } else { - // Unknown address length - return - } - if !r.cryptokey.isValidSource(sourceAddr, addrlen) { - // The packet had a source address that doesn't belong to us or our - // configured crypto-key routing source subnets - return - } - if !destAddr.IsValid() && !destSnet.IsValid() { - // The addresses didn't match valid Yggdrasil node addresses so let's see - // whether it matches a crypto-key routing range instead - if key, err := r.cryptokey.getPublicKeyForAddress(destAddr, addrlen); err == nil { - // A public key was found, get the node ID for the search - destPubKey = &key - destNodeID = crypto.GetNodeID(destPubKey) - // Do a quick check to ensure that the node ID refers to a vaild Yggdrasil - // address or subnet - this might be superfluous - addr := *address.AddrForNodeID(destNodeID) - copy(destAddr[:], addr[:]) - copy(destSnet[:], addr[:]) - if !destAddr.IsValid() && !destSnet.IsValid() { - return - } - } else { - // No public key was found in the CKR table so we've exhausted our options - return - } - } - searchCompleted := func(sinfo *sessionInfo, err error) { - if err != nil { - r.core.log.Debugln("DHT search failed:", err) - return - } - } - doSearch := func(packet []byte) { - var nodeID, mask *crypto.NodeID - switch { - case destNodeID != nil: - // We already know the full node ID, probably because it's from a CKR - // route in which the public key is known ahead of time - nodeID = destNodeID - var m crypto.NodeID - for i := range m { - m[i] = 0xFF - } - mask = &m - case destAddr.IsValid(): - // We don't know the full node ID - try and use the address to generate - // a truncated node ID - nodeID, mask = destAddr.GetNodeIDandMask() - case destSnet.IsValid(): - // We don't know the full node ID - try and use the subnet to generate - // a truncated node ID - nodeID, mask = destSnet.GetNodeIDandMask() - default: - return - } - sinfo, isIn := r.core.searches.searches[*nodeID] - if !isIn { - sinfo = r.core.searches.newIterSearch(nodeID, mask, searchCompleted) - } - if packet != nil { - sinfo.packet = packet - } - r.core.searches.continueSearch(sinfo) - } - var sinfo *sessionInfo - var isIn bool - if destAddr.IsValid() { - sinfo, isIn = r.core.sessions.getByTheirAddr(&destAddr) - } - if destSnet.IsValid() { - sinfo, isIn = r.core.sessions.getByTheirSubnet(&destSnet) - } - sTime := sinfo.time.Load().(time.Time) - pingTime := sinfo.pingTime.Load().(time.Time) - pingSend := sinfo.pingSend.Load().(time.Time) - switch { - case !isIn || !sinfo.init.Load().(bool): - // No or unintiialized session, so we need to search first - doSearch(bs) - case time.Since(sTime) > 6*time.Second: - if sTime.Before(pingTime) && time.Since(pingTime) > 6*time.Second { - // We haven't heard from the dest in a while - // We tried pinging but didn't get a response - // They may have changed coords - // Try searching to discover new coords - // Note that search spam is throttled internally - doSearch(nil) - } else { - // We haven't heard about the dest in a while - now := time.Now() - - if !sTime.Before(pingTime) { - // Update pingTime to start the clock for searches (above) - sinfo.pingTime.Store(now) - } - if time.Since(pingSend) > time.Second { - // Send at most 1 ping per second - sinfo.pingSend.Store(now) - r.core.sessions.sendPingPong(sinfo, false) - } - } - fallthrough // Also send the packet - default: - // If we know the public key ahead of time (i.e. a CKR route) then check - // if the session perm pub key matches before we send the packet to it - if destPubKey != nil { - if !bytes.Equal((*destPubKey)[:], sinfo.theirPermPub[:]) { - return - } - } - - // Drop packets if the session MTU is 0 - this means that one or other - // side probably has their TUN adapter disabled - if sinfo.getMTU() == 0 { - // Don't continue - drop the packet - return - } - // Generate an ICMPv6 Packet Too Big for packets larger than session MTU - if len(bs) > int(sinfo.getMTU()) { - // Get the size of the oversized payload, up to a max of 900 bytes - window := 900 - if int(sinfo.getMTU()) < window { - window = int(sinfo.getMTU()) - } - - // Send the error back to the adapter - r.reject <- RejectedPacket{ - Reason: PacketTooBig, - Packet: bs[:window], - Detail: int(sinfo.getMTU()), - } - - // Don't continue - drop the packet - return - } - sinfo.send <- bs - } -} -*/ - // Checks incoming traffic type and passes it to the appropriate handler. func (r *router) handleIn(packet []byte) { pType, pTypeLen := wire_decode_uint64(packet) diff --git a/src/yggdrasil/wire.go b/src/yggdrasil/wire.go index 1bb4d90..5aa354d 100644 --- a/src/yggdrasil/wire.go +++ b/src/yggdrasil/wire.go @@ -394,7 +394,7 @@ func (p *nodeinfoReqRes) decode(bs []byte) bool { if len(bs) == 0 { return false } - p.NodeInfo = make(nodeinfoPayload, len(bs)) + p.NodeInfo = make(NodeInfoPayload, len(bs)) if !wire_chop_slice(p.NodeInfo[:], &bs) { return false }