From d01662c1fb1a887e9406b05d7c3a62b6561bf94e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 20 Apr 2019 16:32:27 +0100 Subject: [PATCH] Try to convert TUN/TAP to use new yggdrasil.Conn, search masks are still broken --- cmd/yggdrasil/main.go | 237 +++++++++++++++-------------- src/crypto/crypto.go | 27 ++++ src/tuntap/tun.go | 304 +++++++++++++++++++++++++++----------- src/tuntap/tun_bsd.go | 24 +-- src/tuntap/tun_darwin.go | 14 +- src/tuntap/tun_linux.go | 6 +- src/tuntap/tun_other.go | 2 +- src/tuntap/tun_windows.go | 32 ++-- src/yggdrasil/adapter.go | 47 ------ src/yggdrasil/conn.go | 19 ++- src/yggdrasil/core.go | 68 +-------- src/yggdrasil/dialer.go | 70 +++++++++ src/yggdrasil/router.go | 4 - src/yggdrasil/session.go | 14 +- 14 files changed, 502 insertions(+), 366 deletions(-) delete mode 100644 src/yggdrasil/adapter.go create mode 100644 src/yggdrasil/dialer.go diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 0e3aa354..f3d933c2 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -10,7 +10,6 @@ import ( "os/signal" "strings" "syscall" - "time" "golang.org/x/text/encoding/unicode" @@ -77,57 +76,44 @@ func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeCo panic(err) } json.Unmarshal(confJson, &cfg) - // For now we will do a little bit to help the user adjust their - // configuration to match the new configuration format, as some of the key - // names have changed recently. - changes := map[string]string{ - "Multicast": "", - "ReadTimeout": "", - "LinkLocal": "MulticastInterfaces", - "BoxPub": "EncryptionPublicKey", - "BoxPriv": "EncryptionPrivateKey", - "SigPub": "SigningPublicKey", - "SigPriv": "SigningPrivateKey", - "AllowedBoxPubs": "AllowedEncryptionPublicKeys", - } - // Loop over the mappings aove and see if we have anything to fix. - for from, to := range changes { - if _, ok := dat[from]; ok { - if to == "" { - if !*normaliseconf { - log.Println("Warning: Config option", from, "is deprecated") - } - } else { - if !*normaliseconf { - log.Println("Warning: Config option", from, "has been renamed - please change to", to) - } - // If the configuration file doesn't already contain a line with the - // new name then set it to the old value. This makes sure that we - // don't overwrite something that was put there intentionally. - if _, ok := dat[to]; !ok { - dat[to] = dat[from] + /* + // For now we will do a little bit to help the user adjust their + // configuration to match the new configuration format, as some of the key + // names have changed recently. + changes := map[string]string{ + "Multicast": "", + "ReadTimeout": "", + "LinkLocal": "MulticastInterfaces", + "BoxPub": "EncryptionPublicKey", + "BoxPriv": "EncryptionPrivateKey", + "SigPub": "SigningPublicKey", + "SigPriv": "SigningPrivateKey", + "AllowedBoxPubs": "AllowedEncryptionPublicKeys", + } + // Loop over the mappings aove and see if we have anything to fix. + for from, to := range changes { + if _, ok := dat[from]; ok { + if to == "" { + if !*normaliseconf { + log.Println("Warning: Config option", from, "is deprecated") + } + } else { + if !*normaliseconf { + log.Println("Warning: Config option", from, "has been renamed - please change to", to) + } + // If the configuration file doesn't already contain a line with the + // new name then set it to the old value. This makes sure that we + // don't overwrite something that was put there intentionally. + if _, ok := dat[to]; !ok { + dat[to] = dat[from] + } } } } - } - // Check to see if the peers are in a parsable format, if not then default - // them to the TCP scheme - if peers, ok := dat["Peers"].([]interface{}); ok { - for index, peer := range peers { - uri := peer.(string) - if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") { - continue - } - if strings.HasPrefix(uri, "tcp:") { - uri = uri[4:] - } - (dat["Peers"].([]interface{}))[index] = "tcp://" + uri - } - } - // Now do the same with the interface peers - if interfacepeers, ok := dat["InterfacePeers"].(map[string]interface{}); ok { - for intf, peers := range interfacepeers { - for index, peer := range peers.([]interface{}) { + // Check to see if the peers are in a parsable format, if not then default + // them to the TCP scheme + if peers, ok := dat["Peers"].([]interface{}); ok { + for index, peer := range peers { uri := peer.(string) if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") { continue @@ -135,19 +121,34 @@ func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeCo if strings.HasPrefix(uri, "tcp:") { uri = uri[4:] } - ((dat["InterfacePeers"].(map[string]interface{}))[intf]).([]interface{})[index] = "tcp://" + uri + (dat["Peers"].([]interface{}))[index] = "tcp://" + uri } } - } - // Do a quick check for old-format Listen statement so that mapstructure - // doesn't fail and crash - if listen, ok := dat["Listen"].(string); ok { - if strings.HasPrefix(listen, "tcp://") { - dat["Listen"] = []string{listen} - } else { - dat["Listen"] = []string{"tcp://" + listen} + // Now do the same with the interface peers + if interfacepeers, ok := dat["InterfacePeers"].(map[string]interface{}); ok { + for intf, peers := range interfacepeers { + for index, peer := range peers.([]interface{}) { + uri := peer.(string) + if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") { + continue + } + if strings.HasPrefix(uri, "tcp:") { + uri = uri[4:] + } + ((dat["InterfacePeers"].(map[string]interface{}))[intf]).([]interface{})[index] = "tcp://" + uri + } + } } - } + // Do a quick check for old-format Listen statement so that mapstructure + // doesn't fail and crash + if listen, ok := dat["Listen"].(string); ok { + if strings.HasPrefix(listen, "tcp://") { + dat["Listen"] = []string{listen} + } else { + dat["Listen"] = []string{"tcp://" + listen} + } + } + */ // Overlay our newly mapped configuration onto the autoconf node config that // we generated above. if err = mapstructure.Decode(dat, &cfg); err != nil { @@ -249,8 +250,6 @@ func main() { // Setup the Yggdrasil node itself. The node{} type includes a Core, so we // don't need to create this manually. n := node{} - // Before we start the node, set the TUN/TAP to be our router adapter - n.core.SetRouterAdapter(&n.tuntap) // Now start Yggdrasil - this starts the DHT, router, switch and other core // components needed for Yggdrasil to operate state, err := n.core.Start(cfg, logger) @@ -263,72 +262,88 @@ func main() { if err := n.multicast.Start(); err != nil { logger.Errorln("An error occurred starting multicast:", err) } + // Start the TUN/TAP interface + if listener, err := n.core.ConnListen(); err == nil { + if dialer, err := n.core.ConnDialer(); err == nil { + logger.Println("Got listener", listener, "and dialer", dialer) + n.tuntap.Init(state, logger, listener, dialer) + if err := n.tuntap.Start(); err != nil { + logger.Errorln("An error occurred starting TUN/TAP:", err) + } + } else { + logger.Errorln("Unable to get Dialer:", err) + } + } else { + logger.Errorln("Unable to get Listener:", err) + } // The Stop function ensures that the TUN/TAP adapter is correctly shut down // before the program exits. defer func() { n.core.Stop() }() // Listen for new sessions - go func() { - listener, err := n.core.ListenConn() - if err != nil { - logger.Errorln("Unable to listen for sessions:", err) - return - } - for { - conn, err := listener.Accept() - if err != nil { - logger.Errorln("Accept:", err) - continue - } - logger.Println("Accepted") - for { - b := make([]byte, 100) - if n, err := conn.Read(b); err != nil { - logger.Errorln("Read failed:", err) - time.Sleep(time.Second * 2) - } else { - logger.Println("Read", n, "bytes:", b) - b = []byte{5, 5, 5} - if n, err := conn.Write(b); err != nil { - logger.Errorln("Write failed:", err) - time.Sleep(time.Second * 2) - } else { - logger.Println("Wrote", n, "bytes:", b) - } - } - } - } - }() - // Try creating new sessions - go func() { - if cfg.EncryptionPublicKey != "533574224115f835b7c7db6433986bc5aef855ff9c9568c01abeb0fbed3e8810" { - return - } - time.Sleep(time.Second * 2) - conn, err := n.core.Dial("nodeid", "9890e135604e8aa6039a909e40c629824d852042a70e51957d5b9d700195663d50552e8e869af132b4617d76f8ef00314d94cce23aa8d6b051b3b952a32a4966") - if err != nil { - logger.Errorln("Dial:", err) - return - } + /* go func() { + listener, err := n.core.ListenConn() + if err != nil { + logger.Errorln("Unable to listen for sessions:", err) + return + } for { - time.Sleep(time.Second * 2) - b := []byte{1, 2, 3, 4, 5} - if n, err := conn.Write(b); err != nil { - logger.Errorln("Write failed:", err) - } else { - logger.Println("Wrote", n, "bytes:", b) - b = make([]byte, 100) + conn, err := listener.Accept() + if err != nil { + logger.Errorln("Accept:", err) + continue + } + logger.Println("Accepted") + for { + b := make([]byte, 100) if n, err := conn.Read(b); err != nil { logger.Errorln("Read failed:", err) + time.Sleep(time.Second * 2) } else { logger.Println("Read", n, "bytes:", b) + b = []byte{5, 5, 5} + if n, err := conn.Write(b); err != nil { + logger.Errorln("Write failed:", err) + time.Sleep(time.Second * 2) + } else { + logger.Println("Wrote", n, "bytes:", b) + } } } } }() - }() + // Try creating new sessions + go func() { + if cfg.EncryptionPublicKey != "533574224115f835b7c7db6433986bc5aef855ff9c9568c01abeb0fbed3e8810" { + return + } + time.Sleep(time.Second * 2) + conn, err := n.core.Dial("nodeid", "9890e135604e8aa6039a909e40c629824d852042a70e51957d5b9d700195663d50552e8e869af132b4617d76f8ef00314d94cce23aa8d6b051b3b952a32a4966") + if err != nil { + logger.Errorln("Dial:", err) + return + } + go func() { + for { + time.Sleep(time.Second * 2) + b := []byte{1, 2, 3, 4, 5} + if n, err := conn.Write(b); err != nil { + logger.Errorln("Write failed:", err) + } else { + logger.Println("Wrote", n, "bytes:", b) + b = make([]byte, 100) + if n, err := conn.Read(b); err != nil { + logger.Errorln("Read failed:", err) + } else { + logger.Println("Read", n, "bytes:", b) + } + } + } + }() + }() + */ // Make some nice output that tells us what our IPv6 address and subnet are. // This is just logged to stdout for the user. address := n.core.Address() diff --git a/src/crypto/crypto.go b/src/crypto/crypto.go index b47db184..d5b467e9 100644 --- a/src/crypto/crypto.go +++ b/src/crypto/crypto.go @@ -37,10 +37,37 @@ func (n *NodeID) String() string { return hex.EncodeToString(n[:]) } +// Network returns "nodeid" nearly always right now. func (n *NodeID) Network() string { return "nodeid" } +// PrefixLength returns the number of bits set in a masked NodeID. +func (n *NodeID) PrefixLength() int { + var len int + for i, v := range *n { + _, _ = i, v + if v == 0xff { + len += 8 + continue + } + for v&0x80 != 0 { + len++ + v <<= 1 + } + if v != 0 { + return -1 + } + for i++; i < NodeIDLen; i++ { + if n[i] != 0 { + return -1 + } + } + break + } + return len +} + func GetNodeID(pub *BoxPubKey) *NodeID { h := sha512.Sum512(pub[:]) return (*NodeID)(&h) diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index c93b1163..3010fae4 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -3,7 +3,7 @@ package tuntap // This manages the tun driver to send/recv packets to/from applications import ( - "bytes" + "encoding/hex" "errors" "fmt" "net" @@ -11,16 +11,12 @@ import ( "time" "github.com/gologme/log" - "golang.org/x/net/icmp" - "golang.org/x/net/ipv6" - - "github.com/songgao/packets/ethernet" "github.com/yggdrasil-network/water" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/config" + "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/defaults" - "github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" ) @@ -32,14 +28,20 @@ const tun_ETHER_HEADER_LENGTH = 14 // you should pass this object to the yggdrasil.SetRouterAdapter() function // before calling yggdrasil.Start(). type TunAdapter struct { - yggdrasil.Adapter - addr address.Address - subnet address.Subnet - icmpv6 ICMPv6 - mtu int - iface *water.Interface - mutex sync.RWMutex // Protects the below - isOpen bool + config *config.NodeState + log *log.Logger + reconfigure chan chan error + conns map[crypto.NodeID]yggdrasil.Conn + connsMutex sync.RWMutex + listener *yggdrasil.Listener + dialer *yggdrasil.Dialer + addr address.Address + subnet address.Subnet + icmpv6 ICMPv6 + mtu int + iface *water.Interface + mutex sync.RWMutex // Protects the below + isOpen bool } // Gets the maximum supported MTU for the platform based on the defaults in @@ -94,62 +96,48 @@ func MaximumMTU() int { return defaults.GetDefaults().MaximumIfMTU } -// Init initialises the TUN/TAP adapter. -func (tun *TunAdapter) Init(config *config.NodeState, log *log.Logger, send chan<- []byte, recv <-chan []byte, reject <-chan yggdrasil.RejectedPacket) { - tun.Adapter.Init(config, log, send, recv, reject) - tun.icmpv6.Init(tun) - go func() { - for { - e := <-tun.Reconfigure - tun.Config.Mutex.RLock() - updated := tun.Config.Current.IfName != tun.Config.Previous.IfName || - tun.Config.Current.IfTAPMode != tun.Config.Previous.IfTAPMode || - tun.Config.Current.IfMTU != tun.Config.Previous.IfMTU - tun.Config.Mutex.RUnlock() - if updated { - tun.Log.Warnln("Reconfiguring TUN/TAP is not supported yet") - e <- nil - } else { - e <- nil - } - } - }() +// Init initialises the TUN/TAP module. You must have acquired a Listener from +// the Yggdrasil core before this point and it must not be in use elsewhere. +func (tun *TunAdapter) Init(config *config.NodeState, log *log.Logger, listener *yggdrasil.Listener, dialer *yggdrasil.Dialer) { + tun.config = config + tun.log = log + tun.listener = listener + tun.dialer = dialer + tun.conns = make(map[crypto.NodeID]yggdrasil.Conn) } // 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(a address.Address, s address.Subnet) error { - tun.addr = a - tun.subnet = s - if tun.Config == nil { +func (tun *TunAdapter) Start() error { + tun.config.Mutex.Lock() + defer tun.config.Mutex.Unlock() + if tun.config == nil || tun.listener == nil || tun.dialer == nil { return errors.New("No configuration available to TUN/TAP") } - tun.Config.Mutex.RLock() - ifname := tun.Config.Current.IfName - iftapmode := tun.Config.Current.IfTAPMode - addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1) - mtu := tun.Config.Current.IfMTU - tun.Config.Mutex.RUnlock() + var boxPub crypto.BoxPubKey + boxPubHex, err := hex.DecodeString(tun.config.Current.EncryptionPublicKey) + if err != nil { + return err + } + copy(boxPub[:], boxPubHex) + nodeID := crypto.GetNodeID(&boxPub) + tun.addr = *address.AddrForNodeID(nodeID) + tun.subnet = *address.SubnetForNodeID(nodeID) + tun.mtu = tun.config.Current.IfMTU + ifname := tun.config.Current.IfName + iftapmode := tun.config.Current.IfTAPMode if ifname != "none" { - if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil { + if err := tun.setup(ifname, iftapmode, net.IP(tun.addr[:]).String(), tun.mtu); err != nil { return err } } if ifname == "none" || ifname == "dummy" { - tun.Log.Debugln("Not starting TUN/TAP as ifname is none or dummy") + tun.log.Debugln("Not starting TUN/TAP as ifname is none or dummy") return nil } tun.mutex.Lock() tun.isOpen = true tun.mutex.Unlock() - go func() { - tun.Log.Debugln("Starting TUN/TAP reader goroutine") - tun.Log.Errorln("WARNING: tun.read() exited with error:", tun.read()) - }() - go func() { - tun.Log.Debugln("Starting TUN/TAP writer goroutine") - tun.Log.Errorln("WARNING: tun.write() exited with error:", tun.write()) - }() if iftapmode { go func() { for { @@ -167,74 +155,215 @@ func (tun *TunAdapter) Start(a address.Address, s address.Subnet) error { } }() } + tun.icmpv6.Init(tun) + go func() { + for { + e := <-tun.reconfigure + e <- nil + } + }() + go tun.handler() + go tun.ifaceReader() return nil } +func (tun *TunAdapter) handler() error { + for { + // Accept the incoming connection + conn, err := tun.listener.Accept() + if err != nil { + tun.log.Errorln("TUN/TAP error accepting connection:", err) + return err + } + tun.log.Println("Accepted connection from", conn.RemoteAddr()) + go tun.connReader(conn) + } +} + +func (tun *TunAdapter) connReader(conn *yggdrasil.Conn) error { + b := make([]byte, 65535) + for { + n, err := conn.Read(b) + if err != nil { + tun.log.Errorln("TUN/TAP read error:", err) + return err + } + if n == 0 { + continue + } + w, err := tun.iface.Write(b[:n]) + if err != nil { + tun.log.Errorln("TUN/TAP failed to write to interface:", err) + continue + } + if w != n { + tun.log.Errorln("TUN/TAP wrote", w, "instead of", n, "which is bad") + continue + } + } +} + +func (tun *TunAdapter) ifaceReader() error { + tun.log.Println("Start TUN reader") + bs := make([]byte, 65535) + for { + n, err := tun.iface.Read(bs) + if err != nil { + tun.log.Errorln("TUN/TAP iface read error:", err) + } + // Look up if the dstination address is somewhere we already have an + // open connection to + var srcAddr address.Address + var dstAddr address.Address + var dstNodeID *crypto.NodeID + var dstNodeIDMask *crypto.NodeID + var dstSnet address.Subnet + 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(srcAddr[:addrlen], bs[8:]) + copy(dstAddr[:addrlen], bs[24:]) + copy(dstSnet[: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(srcAddr[:addrlen], bs[12:]) + copy(dstAddr[:addrlen], bs[16:]) + } else { + // Unknown address length + continue + } + dstNodeID, dstNodeIDMask = dstAddr.GetNodeIDandMask() + // Do we have an active connection for this node ID? + if conn, isIn := tun.conns[*dstNodeID]; isIn { + fmt.Println("We have a connection for", *dstNodeID) + w, err := conn.Write(bs) + if err != nil { + fmt.Println("Unable to write to remote:", err) + continue + } + if w != n { + continue + } + } else { + fmt.Println("Opening connection for", *dstNodeID) + tun.connsMutex.Lock() + maskstr := hex.EncodeToString(dstNodeID[:]) + masklen := dstNodeIDMask.PrefixLength() + cidr := fmt.Sprintf("%s/%d", maskstr, masklen) + if conn, err := tun.dialer.Dial("nodeid", cidr); err == nil { + tun.conns[*dstNodeID] = conn + go tun.connReader(&conn) + } else { + fmt.Println("Error dialing:", err) + } + tun.connsMutex.Unlock() + } + + /*if !r.cryptokey.isValidSource(srcAddr, addrlen) { + // The packet had a src address that doesn't belong to us or our + // configured crypto-key routing src subnets + return + } + if !dstAddr.IsValid() && !dstSnet.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(dstAddr, addrlen); err == nil { + // A public key was found, get the node ID for the search + dstPubKey = &key + dstNodeID = crypto.GetNodeID(dstPubKey) + // 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(dstNodeID) + copy(dstAddr[:], addr[:]) + copy(dstSnet[:], addr[:]) + if !dstAddr.IsValid() && !dstSnet.IsValid() { + return + } + } else { + // No public key was found in the CKR table so we've exhausted our options + return + } + }*/ + + } +} + // Writes a packet to the TUN/TAP adapter. If the adapter is running in TAP // mode then additional ethernet encapsulation is added for the benefit of the // host operating system. +/* func (tun *TunAdapter) write() error { for { select { case reject := <-tun.Reject: - switch reject.Reason { - case yggdrasil.PacketTooBig: - if mtu, ok := reject.Detail.(int); ok { - // Create the Packet Too Big response - ptb := &icmp.PacketTooBig{ - MTU: int(mtu), - Data: reject.Packet, - } - - // Create the ICMPv6 response from it - icmpv6Buf, err := CreateICMPv6( - reject.Packet[8:24], reject.Packet[24:40], - ipv6.ICMPTypePacketTooBig, 0, ptb) - - // Send the ICMPv6 response back to the TUN/TAP adapter - if err == nil { - tun.iface.Write(icmpv6Buf) - } + switch reject.Reason { + case yggdrasil.PacketTooBig: + if mtu, ok := reject.Detail.(int); ok { + // Create the Packet Too Big response + ptb := &icmp.PacketTooBig{ + MTU: int(mtu), + Data: reject.Packet, + } + + // Create the ICMPv6 response from it + icmpv6Buf, err := CreateICMPv6( + reject.Packet[8:24], reject.Packet[24:40], + ipv6.ICMPTypePacketTooBig, 0, ptb) + + // Send the ICMPv6 response back to the TUN/TAP adapter + if err == nil { + tun.iface.Write(icmpv6Buf) } - fallthrough - default: - continue } + fallthrough + default: + continue + } case data := <-tun.Recv: if tun.iface == nil { continue } if tun.iface.IsTAP() { - var destAddr address.Address + var dstAddr address.Address if data[0]&0xf0 == 0x60 { if len(data) < 40 { //panic("Tried to send a packet shorter than an IPv6 header...") util.PutBytes(data) continue } - copy(destAddr[:16], data[24:]) + copy(dstAddr[:16], data[24:]) } else if data[0]&0xf0 == 0x40 { if len(data) < 20 { //panic("Tried to send a packet shorter than an IPv4 header...") util.PutBytes(data) continue } - copy(destAddr[:4], data[16:]) + copy(dstAddr[:4], data[16:]) } else { return errors.New("Invalid address family") } - sendndp := func(destAddr address.Address) { - neigh, known := tun.icmpv6.peermacs[destAddr] + sendndp := func(dstAddr address.Address) { + neigh, known := tun.icmpv6.peermacs[dstAddr] known = known && (time.Since(neigh.lastsolicitation).Seconds() < 30) if !known { - request, err := tun.icmpv6.CreateNDPL2(destAddr) + request, err := tun.icmpv6.CreateNDPL2(dstAddr) if err != nil { panic(err) } if _, err := tun.iface.Write(request); err != nil { panic(err) } - tun.icmpv6.peermacs[destAddr] = neighbor{ + tun.icmpv6.peermacs[dstAddr] = neighbor{ lastsolicitation: time.Now(), } } @@ -242,19 +371,19 @@ func (tun *TunAdapter) write() error { var peermac macAddress var peerknown bool if data[0]&0xf0 == 0x40 { - destAddr = tun.addr + dstAddr = tun.addr } else if data[0]&0xf0 == 0x60 { - if !bytes.Equal(tun.addr[:16], destAddr[:16]) && !bytes.Equal(tun.subnet[:8], destAddr[:8]) { - destAddr = tun.addr + if !bytes.Equal(tun.addr[:16], dstAddr[:16]) && !bytes.Equal(tun.subnet[:8], dstAddr[:8]) { + dstAddr = tun.addr } } - if neighbor, ok := tun.icmpv6.peermacs[destAddr]; ok && neighbor.learned { + if neighbor, ok := tun.icmpv6.peermacs[dstAddr]; ok && neighbor.learned { peermac = neighbor.mac peerknown = true } else if neighbor, ok := tun.icmpv6.peermacs[tun.addr]; ok && neighbor.learned { peermac = neighbor.mac peerknown = true - sendndp(destAddr) + sendndp(dstAddr) } else { sendndp(tun.addr) } @@ -359,3 +488,4 @@ func (tun *TunAdapter) Close() error { } return tun.iface.Close() } +*/ diff --git a/src/tuntap/tun_bsd.go b/src/tuntap/tun_bsd.go index 27c9bb2f..996f3140 100644 --- a/src/tuntap/tun_bsd.go +++ b/src/tuntap/tun_bsd.go @@ -109,14 +109,14 @@ func (tun *TunAdapter) setupAddress(addr string) error { // Create system socket if sfd, err = unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0); err != nil { - tun.Log.Printf("Create AF_INET socket failed: %v.", err) + tun.log.Printf("Create AF_INET socket failed: %v.", err) return err } // Friendly output - tun.Log.Infof("Interface name: %s", tun.iface.Name()) - tun.Log.Infof("Interface IPv6: %s", addr) - tun.Log.Infof("Interface MTU: %d", tun.mtu) + tun.log.Infof("Interface name: %s", tun.iface.Name()) + tun.log.Infof("Interface IPv6: %s", addr) + tun.log.Infof("Interface MTU: %d", tun.mtu) // Create the MTU request var ir in6_ifreq_mtu @@ -126,15 +126,15 @@ func (tun *TunAdapter) setupAddress(addr string) error { // Set the MTU if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(syscall.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { err = errno - tun.Log.Errorf("Error in SIOCSIFMTU: %v", errno) + tun.log.Errorf("Error in SIOCSIFMTU: %v", errno) // Fall back to ifconfig to set the MTU cmd := exec.Command("ifconfig", tun.iface.Name(), "mtu", string(tun.mtu)) - tun.Log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) + tun.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.Log.Errorf("SIOCSIFMTU fallback failed: %v.", err) - tun.Log.Traceln(string(output)) + tun.log.Errorf("SIOCSIFMTU fallback failed: %v.", err) + tun.log.Traceln(string(output)) } } @@ -155,15 +155,15 @@ func (tun *TunAdapter) setupAddress(addr string) error { // Set the interface address if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(SIOCSIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { err = errno - tun.Log.Errorf("Error in SIOCSIFADDR_IN6: %v", errno) + tun.log.Errorf("Error in SIOCSIFADDR_IN6: %v", errno) // Fall back to ifconfig to set the address cmd := exec.Command("ifconfig", tun.iface.Name(), "inet6", addr) - tun.Log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) + tun.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.Log.Errorf("SIOCSIFADDR_IN6 fallback failed: %v.", err) - tun.Log.Traceln(string(output)) + tun.log.Errorf("SIOCSIFADDR_IN6 fallback failed: %v.", err) + tun.log.Traceln(string(output)) } } diff --git a/src/tuntap/tun_darwin.go b/src/tuntap/tun_darwin.go index 60786b81..5dfca137 100644 --- a/src/tuntap/tun_darwin.go +++ b/src/tuntap/tun_darwin.go @@ -18,7 +18,7 @@ import ( // Configures the "utun" adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { if iftapmode { - tun.Log.Warnln("TAP mode is not supported on this platform, defaulting to TUN") + tun.log.Warnln("TAP mode is not supported on this platform, defaulting to TUN") } config := water.Config{DeviceType: water.TUN} iface, err := water.New(config) @@ -69,7 +69,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { var err error if fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0); err != nil { - tun.Log.Printf("Create AF_SYSTEM socket failed: %v.", err) + tun.log.Printf("Create AF_SYSTEM socket failed: %v.", err) return err } @@ -98,19 +98,19 @@ func (tun *TunAdapter) setupAddress(addr string) error { copy(ir.ifr_name[:], tun.iface.Name()) ir.ifru_mtu = uint32(tun.mtu) - tun.Log.Infof("Interface name: %s", ar.ifra_name) - tun.Log.Infof("Interface IPv6: %s", addr) - tun.Log.Infof("Interface MTU: %d", ir.ifru_mtu) + tun.log.Infof("Interface name: %s", ar.ifra_name) + tun.log.Infof("Interface IPv6: %s", addr) + tun.log.Infof("Interface MTU: %d", ir.ifru_mtu) if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { err = errno - tun.Log.Errorf("Error in darwin_SIOCAIFADDR_IN6: %v", errno) + tun.log.Errorf("Error in darwin_SIOCAIFADDR_IN6: %v", errno) return err } if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { err = errno - tun.Log.Errorf("Error in SIOCSIFMTU: %v", errno) + tun.log.Errorf("Error in SIOCSIFMTU: %v", errno) return err } diff --git a/src/tuntap/tun_linux.go b/src/tuntap/tun_linux.go index 7d228574..c9c03c09 100644 --- a/src/tuntap/tun_linux.go +++ b/src/tuntap/tun_linux.go @@ -40,9 +40,9 @@ func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int } } // Friendly output - tun.Log.Infof("Interface name: %s", tun.iface.Name()) - tun.Log.Infof("Interface IPv6: %s", addr) - tun.Log.Infof("Interface MTU: %d", tun.mtu) + tun.log.Infof("Interface name: %s", tun.iface.Name()) + tun.log.Infof("Interface IPv6: %s", addr) + tun.log.Infof("Interface MTU: %d", tun.mtu) return tun.setupAddress(addr) } diff --git a/src/tuntap/tun_other.go b/src/tuntap/tun_other.go index bb302d19..48276b49 100644 --- a/src/tuntap/tun_other.go +++ b/src/tuntap/tun_other.go @@ -28,6 +28,6 @@ func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int // 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 { - tun.Log.Warnln("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr) + tun.log.Warnln("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr) return nil } diff --git a/src/tuntap/tun_windows.go b/src/tuntap/tun_windows.go index 5a158b14..8a66ac62 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tuntap/tun_windows.go @@ -15,7 +15,7 @@ import ( // delegate the hard work to "netsh". func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { if !iftapmode { - tun.Log.Warnln("TUN mode is not supported on this platform, defaulting to TAP") + tun.log.Warnln("TUN mode is not supported on this platform, defaulting to TAP") } config := water.Config{DeviceType: water.TAP} config.PlatformSpecificParams.ComponentID = "tap0901" @@ -31,19 +31,19 @@ func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int } // Disable/enable the interface to resets its configuration (invalidating iface) cmd := exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=DISABLED") - tun.Log.Printf("netsh command: %v", strings.Join(cmd.Args, " ")) + tun.log.Printf("netsh command: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.Log.Errorf("Windows netsh failed: %v.", err) - tun.Log.Traceln(string(output)) + tun.log.Errorf("Windows netsh failed: %v.", err) + tun.log.Traceln(string(output)) return err } cmd = exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=ENABLED") - tun.Log.Printf("netsh command: %v", strings.Join(cmd.Args, " ")) + tun.log.Printf("netsh command: %v", strings.Join(cmd.Args, " ")) output, err = cmd.CombinedOutput() if err != nil { - tun.Log.Errorf("Windows netsh failed: %v.", err) - tun.Log.Traceln(string(output)) + tun.log.Errorf("Windows netsh failed: %v.", err) + tun.log.Traceln(string(output)) return err } // Get a new iface @@ -58,9 +58,9 @@ func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int panic(err) } // Friendly output - tun.Log.Infof("Interface name: %s", tun.iface.Name()) - tun.Log.Infof("Interface IPv6: %s", addr) - tun.Log.Infof("Interface MTU: %d", tun.mtu) + tun.log.Infof("Interface name: %s", tun.iface.Name()) + tun.log.Infof("Interface IPv6: %s", addr) + tun.log.Infof("Interface MTU: %d", tun.mtu) return tun.setupAddress(addr) } @@ -71,11 +71,11 @@ func (tun *TunAdapter) setupMTU(mtu int) error { fmt.Sprintf("interface=%s", tun.iface.Name()), fmt.Sprintf("mtu=%d", mtu), "store=active") - tun.Log.Debugln("netsh command: %v", strings.Join(cmd.Args, " ")) + tun.log.Debugln("netsh command: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.Log.Errorf("Windows netsh failed: %v.", err) - tun.Log.Traceln(string(output)) + tun.log.Errorf("Windows netsh failed: %v.", err) + tun.log.Traceln(string(output)) return err } return nil @@ -88,11 +88,11 @@ func (tun *TunAdapter) setupAddress(addr string) error { fmt.Sprintf("interface=%s", tun.iface.Name()), fmt.Sprintf("addr=%s", addr), "store=active") - tun.Log.Debugln("netsh command: %v", strings.Join(cmd.Args, " ")) + tun.log.Debugln("netsh command: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.Log.Errorf("Windows netsh failed: %v.", err) - tun.Log.Traceln(string(output)) + tun.log.Errorf("Windows netsh failed: %v.", err) + tun.log.Traceln(string(output)) return err } return nil diff --git a/src/yggdrasil/adapter.go b/src/yggdrasil/adapter.go deleted file mode 100644 index 8fadb195..00000000 --- a/src/yggdrasil/adapter.go +++ /dev/null @@ -1,47 +0,0 @@ -package yggdrasil - -import ( - "github.com/gologme/log" - "github.com/yggdrasil-network/yggdrasil-go/src/address" - "github.com/yggdrasil-network/yggdrasil-go/src/config" -) - -// Adapter defines the minimum required struct members for an adapter type. This -// is now the base type for adapters like tun.go. When implementing a new -// adapter type, you should extend the adapter struct with this one and should -// call the Adapter.Init() function when initialising. -type Adapter struct { - adapterImplementation - Core *Core - Config *config.NodeState - Log *log.Logger - Send chan<- []byte - Recv <-chan []byte - Reject <-chan RejectedPacket - Reconfigure chan chan error -} - -// Defines the minimum required functions for an adapter type. Note that the -// implementation of Init() should call Adapter.Init(). This is not exported -// because doing so breaks the gomobile bindings for iOS/Android. -type adapterImplementation interface { - Init(*config.NodeState, *log.Logger, chan<- []byte, <-chan []byte, <-chan RejectedPacket) - Name() string - MTU() int - IsTAP() bool - Start(address.Address, address.Subnet) error - Close() error -} - -// Init initialises the adapter with the necessary channels to operate from the -// router. When defining a new Adapter type, the Adapter should call this -// function from within it's own Init function to set up the channels. It is -// otherwise not expected for you to call this function directly. -func (adapter *Adapter) Init(config *config.NodeState, log *log.Logger, send chan<- []byte, recv <-chan []byte, reject <-chan RejectedPacket) { - adapter.Config = config - adapter.Log = log - adapter.Send = send - adapter.Recv = recv - adapter.Reject = reject - adapter.Reconfigure = make(chan chan error, 1) -} diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index 874a7a9c..6334eefa 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -16,7 +16,7 @@ type Conn struct { nodeID *crypto.NodeID nodeMask *crypto.NodeID session *sessionInfo - sessionMutex *sync.RWMutex + mutex *sync.RWMutex readDeadline time.Time writeDeadline time.Time expired bool @@ -30,9 +30,10 @@ func (c *Conn) startSearch() { return } if sinfo != nil { - c.sessionMutex.Lock() + c.mutex.Lock() c.session = sinfo - c.sessionMutex.Unlock() + c.nodeID, c.nodeMask = sinfo.theirAddr.GetNodeIDandMask() + c.mutex.Unlock() } } doSearch := func() { @@ -65,8 +66,8 @@ func (c *Conn) startSearch() { } func (c *Conn) Read(b []byte) (int, error) { - c.sessionMutex.RLock() - defer c.sessionMutex.RUnlock() + c.mutex.RLock() + defer c.mutex.RUnlock() if c.expired { return 0, errors.New("session is closed") } @@ -119,8 +120,8 @@ func (c *Conn) Read(b []byte) (int, error) { } func (c *Conn) Write(b []byte) (bytesWritten int, err error) { - c.sessionMutex.RLock() - defer c.sessionMutex.RUnlock() + c.mutex.RLock() + defer c.mutex.RUnlock() if c.expired { return 0, errors.New("session is closed") } @@ -175,7 +176,9 @@ func (c *Conn) LocalAddr() crypto.NodeID { } func (c *Conn) RemoteAddr() crypto.NodeID { - return *crypto.GetNodeID(&c.session.theirPermPub) + c.mutex.RLock() + defer c.mutex.RUnlock() + return *c.nodeID } func (c *Conn) SetDeadline(t time.Time) error { diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index cfba833b..81be1b28 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -5,7 +5,6 @@ import ( "errors" "io/ioutil" "net" - "sync" "time" "github.com/gologme/log" @@ -77,7 +76,6 @@ func (c *Core) init() error { c.searches.init(c) c.dht.init(c) c.sessions.init(c) - //c.multicast.init(c) c.peers.init(c) c.router.init(c) c.switchTable.init(c) // TODO move before peers? before router? @@ -168,21 +166,6 @@ func BuildVersion() string { return buildVersion } -// SetRouterAdapter instructs Yggdrasil to use the given adapter when starting -// the router. The adapter must implement the standard -// adapter.adapterImplementation interface and should extend the adapter.Adapter -// struct. -func (c *Core) SetRouterAdapter(adapter interface{}) error { - // We do this because adapterImplementation is not a valid type for the - // gomobile bindings so we just ask for a generic interface and try to cast it - // to adapterImplementation instead - if a, ok := adapter.(adapterImplementation); ok { - c.router.adapter = a - return nil - } - return errors.New("unsuitable adapter") -} - // Start starts up Yggdrasil using the provided config.NodeConfig, and outputs // debug logging through the provided log.Logger. The started stack will include // TCP and UDP sockets, a multicast discovery socket, an admin socket, router, @@ -233,13 +216,6 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) (*config.NodeState, return nil, err } - if c.router.adapter != nil { - if err := c.router.adapter.Start(c.router.addr, c.router.subnet); err != nil { - c.log.Errorln("Failed to start TUN/TAP") - return nil, err - } - } - go c.addPeerLoop() c.log.Infoln("Startup complete") @@ -249,14 +225,11 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) (*config.NodeState, // Stop shuts down the Yggdrasil node. func (c *Core) Stop() { c.log.Infoln("Stopping...") - if c.router.adapter != nil { - c.router.adapter.Close() - } c.admin.close() } // ListenConn returns a listener for Yggdrasil session connections. -func (c *Core) ListenConn() (*Listener, error) { +func (c *Core) ConnListen() (*Listener, error) { c.sessions.listenerMutex.Lock() defer c.sessions.listenerMutex.Unlock() if c.sessions.listener != nil { @@ -270,40 +243,11 @@ func (c *Core) ListenConn() (*Listener, error) { return c.sessions.listener, nil } -// Dial opens a session to the given node. The first paramter should be "nodeid" -// and the second parameter should contain a hexadecimal representation of the -// target node ID. -func (c *Core) Dial(network, address string) (Conn, error) { - conn := Conn{ - sessionMutex: &sync.RWMutex{}, - } - nodeID := crypto.NodeID{} - nodeMask := crypto.NodeID{} - // Process - switch network { - case "nodeid": - // A node ID was provided - we don't need to do anything special with it - dest, err := hex.DecodeString(address) - if err != nil { - return Conn{}, err - } - copy(nodeID[:], dest) - for i := range nodeMask { - nodeMask[i] = 0xFF - } - default: - // An unexpected address type was given, so give up - return Conn{}, errors.New("unexpected address type") - } - conn.core = c - conn.nodeID = &nodeID - conn.nodeMask = &nodeMask - conn.core.router.doAdmin(func() { - conn.startSearch() - }) - conn.sessionMutex.Lock() - defer conn.sessionMutex.Unlock() - return conn, nil +// ConnDialer returns a dialer for Yggdrasil session connections. +func (c *Core) ConnDialer() (*Dialer, error) { + return &Dialer{ + core: c, + }, nil } // ListenTCP starts a new TCP listener. The input URI should match that of the diff --git a/src/yggdrasil/dialer.go b/src/yggdrasil/dialer.go new file mode 100644 index 00000000..7042bd0e --- /dev/null +++ b/src/yggdrasil/dialer.go @@ -0,0 +1,70 @@ +package yggdrasil + +import ( + "encoding/hex" + "errors" + "fmt" + "strconv" + "strings" + "sync" + + "github.com/yggdrasil-network/yggdrasil-go/src/crypto" +) + +// Dialer represents an Yggdrasil connection dialer. +type Dialer struct { + core *Core +} + +// Dial opens a session to the given node. The first paramter should be "nodeid" +// and the second parameter should contain a hexadecimal representation of the +// target node ID. +func (d *Dialer) Dial(network, address string) (Conn, error) { + conn := Conn{ + mutex: &sync.RWMutex{}, + } + nodeID := crypto.NodeID{} + nodeMask := crypto.NodeID{} + // Process + switch network { + case "nodeid": + // A node ID was provided - we don't need to do anything special with it + if tokens := strings.Split(address, "/"); len(tokens) == 2 { + len, err := strconv.Atoi(tokens[1]) + if err != nil { + return Conn{}, err + } + dest, err := hex.DecodeString(tokens[0]) + if err != nil { + return Conn{}, err + } + copy(nodeID[:], dest) + for idx := 0; idx < len; idx++ { + nodeMask[idx/8] |= 0x80 >> byte(idx%8) + } + fmt.Println(nodeID) + fmt.Println(nodeMask) + } else { + dest, err := hex.DecodeString(tokens[0]) + if err != nil { + return Conn{}, err + } + copy(nodeID[:], dest) + for i := range nodeMask { + nodeMask[i] = 0xFF + } + } + default: + // An unexpected address type was given, so give up + return Conn{}, errors.New("unexpected address type") + } + conn.core = d.core + conn.nodeID = &nodeID + conn.nodeMask = &nodeMask + conn.core.router.doAdmin(func() { + conn.startSearch() + }) + conn.mutex.Lock() + defer conn.mutex.Unlock() + return conn, nil +} diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 693fba44..19b62f91 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -41,7 +41,6 @@ type router struct { in <-chan []byte // packets we received from the network, link to peer's "out" out func([]byte) // packets we're sending to the network, link to peer's "in" toRecv chan router_recvPacket // packets to handle via recvPacket() - adapter adapterImplementation // TUN/TAP adapter recv chan<- []byte // place where the adapter pulls received packets from send <-chan []byte // place where the adapter puts outgoing packets reject chan<- RejectedPacket // place where we send error packets back to adapter @@ -136,9 +135,6 @@ func (r *router) init(core *Core) { r.nodeinfo.setNodeInfo(r.core.config.Current.NodeInfo, r.core.config.Current.NodeInfoPrivacy) r.core.config.Mutex.RUnlock() r.cryptokey.init(r.core) - if r.adapter != nil { - r.adapter.Init(&r.core.config, r.core.log, send, recv, reject) - } } // Starts the mainLoop goroutine. diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 9c09532b..3bf69a8c 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -289,9 +289,7 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo { sinfo.mySesPriv = *priv sinfo.myNonce = *crypto.NewBoxNonce() sinfo.theirMTU = 1280 - if ss.core.router.adapter != nil { - sinfo.myMTU = uint16(ss.core.router.adapter.MTU()) - } + sinfo.myMTU = 1280 now := time.Now() sinfo.timeMutex.Lock() sinfo.time = now @@ -480,11 +478,11 @@ func (ss *sessions) handlePing(ping *sessionPing) { ss.listenerMutex.Lock() if ss.listener != nil { conn := &Conn{ - core: ss.core, - session: sinfo, - sessionMutex: &sync.RWMutex{}, - nodeID: crypto.GetNodeID(&sinfo.theirPermPub), - nodeMask: &crypto.NodeID{}, + core: ss.core, + session: sinfo, + mutex: &sync.RWMutex{}, + nodeID: crypto.GetNodeID(&sinfo.theirPermPub), + nodeMask: &crypto.NodeID{}, } for i := range conn.nodeMask { conn.nodeMask[i] = 0xFF