diff --git a/src/address/address.go b/src/address/address.go index ff2d212..7add23a 100644 --- a/src/address/address.go +++ b/src/address/address.go @@ -112,3 +112,35 @@ func SubnetForKey(publicKey ed25519.PublicKey) *Subnet { snet[len(prefix)-1] |= 0x01 return &snet } + +// GetKet returns the partial ed25519.PublicKey for the Address. +// This is used for key lookup. +func (a *Address) GetKey() ed25519.PublicKey { + var key [ed25519.PublicKeySize]byte + prefix := GetPrefix() + ones := int(a[len(prefix)]) + for idx := 0; idx < ones; idx++ { + key[idx/8] |= 0x80 >> byte(idx%8) + } + keyOffset := ones + 1 + addrOffset := 8*len(prefix) + 8 + for idx := addrOffset; idx < 8*len(a); idx++ { + bits := a[idx/8] & (0x80 >> byte(idx%8)) + bits <<= byte(idx % 8) + keyIdx := keyOffset + (idx - addrOffset) + bits >>= byte(keyIdx % 8) + key[keyIdx/8] |= bits + } + for idx := range key { + key[idx] = ^key[idx] + } + return ed25519.PublicKey(key[:]) +} + +// GetKet returns the partial ed25519.PublicKey for the Subnet. +// This is used for key lookup. +func (s *Subnet) GetKey() ed25519.PublicKey { + var addr Address + copy(addr[:], s[:]) + return addr.GetKey() +} diff --git a/src/admin/admin.go b/src/admin/admin.go index dbf973e..a87e124 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -1,24 +1,24 @@ package admin import ( - "encoding/hex" + //"encoding/hex" "encoding/json" "errors" "fmt" "net" "net/url" "os" - "strconv" + //"strconv" "strings" "time" "github.com/gologme/log" - "github.com/yggdrasil-network/yggdrasil-go/src/address" + //"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/util" - "github.com/yggdrasil-network/yggdrasil-go/src/version" + //"github.com/yggdrasil-network/yggdrasil-go/src/crypto" + //"github.com/yggdrasil-network/yggdrasil-go/src/util" + //"github.com/yggdrasil-network/yggdrasil-go/src/version" "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" ) @@ -82,13 +82,14 @@ func (a *AdminSocket) UpdateConfig(config *config.NodeConfig) { } func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) { + /* TODO a.AddHandler("getSelf", []string{}, func(in Info) (Info, error) { ip := a.core.Address().String() subnet := a.core.Subnet() return Info{ "self": Info{ ip: Info{ - "box_pub_key": a.core.EncryptionPublicKey(), + // TODO"box_pub_key": a.core.EncryptionPublicKey(), "build_name": version.BuildName(), "build_version": version.BuildVersion(), "coords": fmt.Sprintf("%v", a.core.Coords()), @@ -140,203 +141,205 @@ func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) { return Info{"switchqueues": queues.asMap()}, nil }) */ - a.AddHandler("getDHT", []string{}, func(in Info) (Info, error) { - dht := make(Info) - for _, d := range a.core.GetDHT() { - panic("TODO") - addr := new(address.Address) // TODO *address.AddrForNodeID(crypto.GetNodeID(&d.PublicKey)) - so := net.IP(addr[:]).String() - dht[so] = Info{ - "coords": fmt.Sprintf("%v", d.Coords), - "last_seen": d.LastSeen.Seconds(), - "box_pub_key": hex.EncodeToString(d.PublicKey[:]), + /* + a.AddHandler("getDHT", []string{}, func(in Info) (Info, error) { + dht := make(Info) + for _, d := range a.core.GetDHT() { + panic("TODO") + addr := new(address.Address) // TODO *address.AddrForNodeID(crypto.GetNodeID(&d.PublicKey)) + so := net.IP(addr[:]).String() + dht[so] = Info{ + "coords": fmt.Sprintf("%v", d.Coords), + "last_seen": d.LastSeen.Seconds(), + "box_pub_key": hex.EncodeToString(d.PublicKey[:]), + } } - } - return Info{"dht": dht}, nil - }) - a.AddHandler("getSessions", []string{}, func(in Info) (Info, error) { - sessions := make(Info) - for _, s := range a.core.GetSessions() { - panic("TODO") - addr := new(address.Address) //*address.AddrForNodeID(crypto.GetNodeID(&s.PublicKey)) - so := net.IP(addr[:]).String() - sessions[so] = Info{ - "coords": fmt.Sprintf("%v", s.Coords), - "bytes_sent": s.BytesSent, - "bytes_recvd": s.BytesRecvd, - "mtu": s.MTU, - "uptime": s.Uptime.Seconds(), - "was_mtu_fixed": s.WasMTUFixed, - "box_pub_key": hex.EncodeToString(s.PublicKey[:]), + return Info{"dht": dht}, nil + }) + a.AddHandler("getSessions", []string{}, func(in Info) (Info, error) { + sessions := make(Info) + for _, s := range a.core.GetSessions() { + panic("TODO") + addr := new(address.Address) //*address.AddrForNodeID(crypto.GetNodeID(&s.PublicKey)) + so := net.IP(addr[:]).String() + sessions[so] = Info{ + "coords": fmt.Sprintf("%v", s.Coords), + "bytes_sent": s.BytesSent, + "bytes_recvd": s.BytesRecvd, + "mtu": s.MTU, + "uptime": s.Uptime.Seconds(), + "was_mtu_fixed": s.WasMTUFixed, + "box_pub_key": hex.EncodeToString(s.PublicKey[:]), + } + } + return Info{"sessions": sessions}, nil + }) + a.AddHandler("addPeer", []string{"uri", "[interface]"}, func(in Info) (Info, error) { + // Set sane defaults + intf := "" + // Has interface been specified? + if itf, ok := in["interface"]; ok { + intf = itf.(string) + } + if a.core.AddPeer(in["uri"].(string), intf) == nil { + return Info{ + "added": []string{ + in["uri"].(string), + }, + }, nil } - } - return Info{"sessions": sessions}, nil - }) - a.AddHandler("addPeer", []string{"uri", "[interface]"}, func(in Info) (Info, error) { - // Set sane defaults - intf := "" - // Has interface been specified? - if itf, ok := in["interface"]; ok { - intf = itf.(string) - } - if a.core.AddPeer(in["uri"].(string), intf) == nil { return Info{ - "added": []string{ + "not_added": []string{ in["uri"].(string), }, - }, nil - } - return Info{ - "not_added": []string{ - in["uri"].(string), - }, - }, errors.New("Failed to add peer") - }) - a.AddHandler("disconnectPeer", []string{"port"}, func(in Info) (Info, error) { - port, err := strconv.ParseInt(fmt.Sprint(in["port"]), 10, 64) - if err != nil { - return Info{}, err - } - if a.core.DisconnectPeer(uint64(port)) == nil { - return Info{ - "disconnected": []string{ - fmt.Sprint(port), - }, - }, nil - } else { - return Info{ - "not_disconnected": []string{ - fmt.Sprint(port), - }, - }, errors.New("Failed to disconnect peer") - } - }) - a.AddHandler("removePeer", []string{"uri", "[interface]"}, func(in Info) (Info, error) { - // Set sane defaults - intf := "" - // Has interface been specified? - if itf, ok := in["interface"]; ok { - intf = itf.(string) - } - if a.core.RemovePeer(in["uri"].(string), intf) == nil { - return Info{ - "removed": []string{ - in["uri"].(string), - }, - }, nil - } else { + }, errors.New("Failed to add peer") + }) + a.AddHandler("disconnectPeer", []string{"port"}, func(in Info) (Info, error) { + port, err := strconv.ParseInt(fmt.Sprint(in["port"]), 10, 64) + if err != nil { + return Info{}, err + } + if a.core.DisconnectPeer(uint64(port)) == nil { + return Info{ + "disconnected": []string{ + fmt.Sprint(port), + }, + }, nil + } else { + return Info{ + "not_disconnected": []string{ + fmt.Sprint(port), + }, + }, errors.New("Failed to disconnect peer") + } + }) + a.AddHandler("removePeer", []string{"uri", "[interface]"}, func(in Info) (Info, error) { + // Set sane defaults + intf := "" + // Has interface been specified? + if itf, ok := in["interface"]; ok { + intf = itf.(string) + } + if a.core.RemovePeer(in["uri"].(string), intf) == nil { + return Info{ + "removed": []string{ + in["uri"].(string), + }, + }, nil + } else { + return Info{ + "not_removed": []string{ + in["uri"].(string), + }, + }, errors.New("Failed to remove peer") + } return Info{ "not_removed": []string{ in["uri"].(string), }, }, errors.New("Failed to remove peer") - } - return Info{ - "not_removed": []string{ - in["uri"].(string), - }, - }, errors.New("Failed to remove peer") - }) - a.AddHandler("getAllowedEncryptionPublicKeys", []string{}, func(in Info) (Info, error) { - return Info{"allowed_box_pubs": a.core.GetAllowedEncryptionPublicKeys()}, nil - }) - a.AddHandler("addAllowedEncryptionPublicKey", []string{"box_pub_key"}, func(in Info) (Info, error) { - if a.core.AddAllowedEncryptionPublicKey(in["box_pub_key"].(string)) == nil { + }) + a.AddHandler("getAllowedEncryptionPublicKeys", []string{}, func(in Info) (Info, error) { + return Info{"allowed_box_pubs": a.core.GetAllowedEncryptionPublicKeys()}, nil + }) + a.AddHandler("addAllowedEncryptionPublicKey", []string{"box_pub_key"}, func(in Info) (Info, error) { + if a.core.AddAllowedEncryptionPublicKey(in["box_pub_key"].(string)) == nil { + return Info{ + "added": []string{ + in["box_pub_key"].(string), + }, + }, nil + } return Info{ - "added": []string{ + "not_added": []string{ in["box_pub_key"].(string), }, - }, nil - } - return Info{ - "not_added": []string{ - in["box_pub_key"].(string), - }, - }, errors.New("Failed to add allowed key") - }) - a.AddHandler("removeAllowedEncryptionPublicKey", []string{"box_pub_key"}, func(in Info) (Info, error) { - if a.core.RemoveAllowedEncryptionPublicKey(in["box_pub_key"].(string)) == nil { + }, errors.New("Failed to add allowed key") + }) + a.AddHandler("removeAllowedEncryptionPublicKey", []string{"box_pub_key"}, func(in Info) (Info, error) { + if a.core.RemoveAllowedEncryptionPublicKey(in["box_pub_key"].(string)) == nil { + return Info{ + "removed": []string{ + in["box_pub_key"].(string), + }, + }, nil + } return Info{ - "removed": []string{ + "not_removed": []string{ in["box_pub_key"].(string), }, - }, nil - } - return Info{ - "not_removed": []string{ - in["box_pub_key"].(string), - }, - }, errors.New("Failed to remove allowed key") - }) - a.AddHandler("dhtPing", []string{"box_pub_key", "coords", "[target]"}, func(in Info) (Info, error) { - var reserr error - var result yggdrasil.DHTRes - if in["target"] == nil { - in["target"] = "none" - } - coords := util.DecodeCoordString(in["coords"].(string)) - var boxPubKey crypto.BoxPubKey - if b, err := hex.DecodeString(in["box_pub_key"].(string)); err == nil { - copy(boxPubKey[:], b) - if n, err := hex.DecodeString(in["target"].(string)); err == nil { - var targetNodeID crypto.NodeID - copy(targetNodeID[:], n) - result, reserr = a.core.DHTPing(boxPubKey, coords, &targetNodeID) - } else { - result, reserr = a.core.DHTPing(boxPubKey, coords, nil) + }, errors.New("Failed to remove allowed key") + }) + a.AddHandler("dhtPing", []string{"box_pub_key", "coords", "[target]"}, func(in Info) (Info, error) { + var reserr error + var result yggdrasil.DHTRes + if in["target"] == nil { + in["target"] = "none" } - } else { - return Info{}, err - } - if reserr != nil { - return Info{}, reserr - } - infos := make(map[string]map[string]string, len(result.Infos)) - for _, dinfo := range result.Infos { - info := map[string]string{ - "box_pub_key": hex.EncodeToString(dinfo.PublicKey[:]), - "coords": fmt.Sprintf("%v", dinfo.Coords), - } - panic("TODO") - addr := "" //net.IP(address.AddrForNodeID(crypto.GetNodeID(&dinfo.PublicKey))[:]).String() - infos[addr] = info - } - return Info{"nodes": infos}, nil - }) - a.AddHandler("getNodeInfo", []string{"[box_pub_key]", "[coords]", "[nocache]"}, func(in Info) (Info, error) { - var nocache bool - if in["nocache"] != nil { - nocache = in["nocache"].(string) == "true" - } - var boxPubKey crypto.BoxPubKey - var coords []uint64 - if in["box_pub_key"] == nil && in["coords"] == nil { - nodeinfo := a.core.MyNodeInfo() - var jsoninfo interface{} - if err := json.Unmarshal(nodeinfo, &jsoninfo); err != nil { - return Info{}, err - } - return Info{"nodeinfo": jsoninfo}, nil - } else if in["box_pub_key"] == nil || in["coords"] == nil { - return Info{}, errors.New("Expecting both box_pub_key and coords") - } else { + coords := util.DecodeCoordString(in["coords"].(string)) + var boxPubKey crypto.BoxPubKey if b, err := hex.DecodeString(in["box_pub_key"].(string)); err == nil { copy(boxPubKey[:], b) + if n, err := hex.DecodeString(in["target"].(string)); err == nil { + var targetNodeID crypto.NodeID + copy(targetNodeID[:], n) + result, reserr = a.core.DHTPing(boxPubKey, coords, &targetNodeID) + } else { + result, reserr = a.core.DHTPing(boxPubKey, coords, nil) + } } else { return Info{}, err } - coords = util.DecodeCoordString(in["coords"].(string)) - } - result, err := a.core.GetNodeInfo(boxPubKey, coords, nocache) - if err == nil { - var m map[string]interface{} - if err = json.Unmarshal(result, &m); err == nil { - return Info{"nodeinfo": m}, nil + if reserr != nil { + return Info{}, reserr + } + infos := make(map[string]map[string]string, len(result.Infos)) + for _, dinfo := range result.Infos { + info := map[string]string{ + "box_pub_key": hex.EncodeToString(dinfo.PublicKey[:]), + "coords": fmt.Sprintf("%v", dinfo.Coords), + } + panic("TODO") + addr := "" //net.IP(address.AddrForNodeID(crypto.GetNodeID(&dinfo.PublicKey))[:]).String() + infos[addr] = info + } + return Info{"nodes": infos}, nil + }) + a.AddHandler("getNodeInfo", []string{"[box_pub_key]", "[coords]", "[nocache]"}, func(in Info) (Info, error) { + var nocache bool + if in["nocache"] != nil { + nocache = in["nocache"].(string) == "true" + } + var boxPubKey crypto.BoxPubKey + var coords []uint64 + if in["box_pub_key"] == nil && in["coords"] == nil { + nodeinfo := a.core.MyNodeInfo() + var jsoninfo interface{} + if err := json.Unmarshal(nodeinfo, &jsoninfo); err != nil { + return Info{}, err + } + return Info{"nodeinfo": jsoninfo}, nil + } else if in["box_pub_key"] == nil || in["coords"] == nil { + return Info{}, errors.New("Expecting both box_pub_key and coords") + } else { + if b, err := hex.DecodeString(in["box_pub_key"].(string)); err == nil { + copy(boxPubKey[:], b) + } else { + return Info{}, err + } + coords = util.DecodeCoordString(in["coords"].(string)) + } + result, err := a.core.GetNodeInfo(boxPubKey, coords, nocache) + if err == nil { + var m map[string]interface{} + if err = json.Unmarshal(result, &m); err == nil { + return Info{"nodeinfo": m}, nil + } + return Info{}, err } return Info{}, err - } - return Info{}, err - }) + }) + */ } // Start runs the admin API socket to listen for / respond to admin API calls. diff --git a/src/tuntap/conn.go b/src/tuntap/conn.go deleted file mode 100644 index ddd89e9..0000000 --- a/src/tuntap/conn.go +++ /dev/null @@ -1,227 +0,0 @@ -package tuntap - -import ( - "bytes" - "errors" - "time" - - "github.com/Arceliar/phony" - "github.com/yggdrasil-network/yggdrasil-go/src/address" - "github.com/yggdrasil-network/yggdrasil-go/src/crypto" - "github.com/yggdrasil-network/yggdrasil-go/src/util" - "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" - "golang.org/x/net/icmp" - "golang.org/x/net/ipv6" -) - -const tunConnTimeout = 2 * time.Minute - -type tunConn struct { - phony.Inbox - tun *TunAdapter - conn *yggdrasil.Conn - addr address.Address - snet address.Subnet - stop chan struct{} - alive *time.Timer // From calling time.AfterFunc -} - -func (s *tunConn) close() { - s.tun.Act(s, s._close_from_tun) -} - -func (s *tunConn) _close_from_tun() { - go s.conn.Close() // Just in case it blocks on actor operations - delete(s.tun.addrToConn, s.addr) - delete(s.tun.subnetToConn, s.snet) - func() { - defer func() { recover() }() - close(s.stop) // Closes reader/writer goroutines - }() -} - -func (s *tunConn) _read(bs []byte) (err error) { - select { - case <-s.stop: - err = errors.New("session was already closed") - return - default: - } - if len(bs) == 0 { - err = errors.New("read packet with 0 size") - return - } - ipv4 := len(bs) > 20 && bs[0]&0xf0 == 0x40 - ipv6 := len(bs) > 40 && bs[0]&0xf0 == 0x60 - isCGA := true - // Check source addresses - switch { - case ipv6 && bs[8] == 0x02 && bytes.Equal(s.addr[:16], bs[8:24]): // source - case ipv6 && bs[8] == 0x03 && bytes.Equal(s.snet[:8], bs[8:16]): // source - default: - isCGA = false - } - // Check destination addresses - switch { - case ipv6 && bs[24] == 0x02 && bytes.Equal(s.tun.addr[:16], bs[24:40]): // destination - case ipv6 && bs[24] == 0x03 && bytes.Equal(s.tun.subnet[:8], bs[24:32]): // destination - default: - isCGA = false - } - // Decide how to handle the packet - var skip bool - switch { - case isCGA: // Allowed - case s.tun.ckr.isEnabled() && (ipv4 || ipv6): - var srcAddr address.Address - var dstAddr address.Address - var addrlen int - if ipv4 { - copy(srcAddr[:], bs[12:16]) - copy(dstAddr[:], bs[16:20]) - addrlen = 4 - } - if ipv6 { - copy(srcAddr[:], bs[8:24]) - copy(dstAddr[:], bs[24:40]) - addrlen = 16 - } - if !s.tun.ckr.isValidLocalAddress(dstAddr, addrlen) { - // The destination address isn't in our CKR allowed range - skip = true - } else if key, err := s.tun.ckr.getPublicKeyForAddress(srcAddr, addrlen); err == nil { - if *s.conn.RemoteAddr().(*crypto.BoxPubKey) == key { - // This is the one allowed CKR case, where source and destination addresses are both good - } else { - // The CKR key associated with this address doesn't match the sender's NodeID - skip = true - } - } else { - // We have no CKR route for this source address - skip = true - } - default: - skip = true - } - if skip { - err = errors.New("address not allowed") - return - } - s.tun.writer.writeFrom(s, bs) - s.stillAlive() - return -} - -func (s *tunConn) writeFrom(from phony.Actor, bs []byte) { - s.Act(from, func() { - s._write(bs) - }) -} - -func (s *tunConn) _write(bs []byte) (err error) { - select { - case <-s.stop: - err = errors.New("session was already closed") - return - default: - } - v4 := len(bs) > 20 && bs[0]&0xf0 == 0x40 - v6 := len(bs) > 40 && bs[0]&0xf0 == 0x60 - isCGA := true - // Check source addresses - switch { - case v6 && bs[8] == 0x02 && bytes.Equal(s.tun.addr[:16], bs[8:24]): // source - case v6 && bs[8] == 0x03 && bytes.Equal(s.tun.subnet[:8], bs[8:16]): // source - default: - isCGA = false - } - // Check destiantion addresses - switch { - case v6 && bs[24] == 0x02 && bytes.Equal(s.addr[:16], bs[24:40]): // destination - case v6 && bs[24] == 0x03 && bytes.Equal(s.snet[:8], bs[24:32]): // destination - default: - isCGA = false - } - // Decide how to handle the packet - var skip bool - switch { - case isCGA: // Allowed - case s.tun.ckr.isEnabled() && (v4 || v6): - var srcAddr address.Address - var dstAddr address.Address - var addrlen int - if v4 { - copy(srcAddr[:], bs[12:16]) - copy(dstAddr[:], bs[16:20]) - addrlen = 4 - } - if v6 { - copy(srcAddr[:], bs[8:24]) - copy(dstAddr[:], bs[24:40]) - addrlen = 16 - } - if !s.tun.ckr.isValidLocalAddress(srcAddr, addrlen) { - // The source address isn't in our CKR allowed range - skip = true - } else if key, err := s.tun.ckr.getPublicKeyForAddress(dstAddr, addrlen); err == nil { - if *s.conn.RemoteAddr().(*crypto.BoxPubKey) == key { - // This is the one allowed CKR case, where source and destination addresses are both good - } else { - // The CKR key associated with this address doesn't match the sender's NodeID - skip = true - } - } else { - // We have no CKR route for this destination address... why do we have the packet in the first place? - skip = true - } - default: - skip = true - } - if skip { - err = errors.New("address not allowed") - return - } - msg := yggdrasil.FlowKeyMessage{ - FlowKey: util.GetFlowKey(bs), - Message: bs, - } - s.conn.WriteFrom(s, msg, func(err error) { - if err == nil { - // No point in wasting resources to send back an error if there was none - return - } - s.Act(s.conn, func() { - if e, eok := err.(yggdrasil.ConnError); !eok { - if e.Closed() { - s.tun.log.Debugln(s.conn.String(), "TUN/TAP generic write debug:", err) - } else { - s.tun.log.Errorln(s.conn.String(), "TUN/TAP generic write error:", err) - } - } else if e.PacketTooBig() { - // TODO: This currently isn't aware of IPv4 for CKR - ptb := &icmp.PacketTooBig{ - MTU: int(e.PacketMaximumSize()), - Data: bs[:900], - } - if packet, err := CreateICMPv6(bs[8:24], bs[24:40], ipv6.ICMPTypePacketTooBig, 0, ptb); err == nil { - s.tun.writer.writeFrom(s, packet) - } - } else { - if e.Closed() { - s.tun.log.Debugln(s.conn.String(), "TUN/TAP conn write debug:", err) - } else { - s.tun.log.Errorln(s.conn.String(), "TUN/TAP conn write error:", err) - } - } - }) - }) - s.stillAlive() - return -} - -func (s *tunConn) stillAlive() { - if s.alive != nil { - s.alive.Stop() - } - s.alive = time.AfterFunc(tunConnTimeout, s.close) -} diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index da6d8e2..836a9c6 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -1,12 +1,12 @@ package tuntap import ( - "github.com/yggdrasil-network/yggdrasil-go/src/address" + //"github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" - "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" + //"github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" - "golang.org/x/net/icmp" - "golang.org/x/net/ipv6" + //"golang.org/x/net/icmp" + //"golang.org/x/net/ipv6" "github.com/Arceliar/phony" ) @@ -84,139 +84,142 @@ func (tun *TunAdapter) handlePacketFrom(from phony.Actor, packet []byte, err err // does the work of reading a packet and sending it to the correct tunConn func (tun *TunAdapter) _handlePacket(recvd []byte, err error) { - if err != nil { - tun.log.Errorln("TUN iface read error:", err) - return - } - // Offset the buffer from now on so that we can ignore ethernet frames if - // they are present - bs := recvd[:] - // Check if the packet is long enough to detect if it's an ICMP packet or not - if len(bs) < 7 { - tun.log.Traceln("TUN iface read undersized unknown packet, length:", len(bs)) - return - } - // From the IP header, work out what our source and destination addresses - // and node IDs are. We will need these in order to work out where to send - // the packet - var dstAddr address.Address - var dstSnet address.Subnet - var addrlen int - n := len(bs) - // Check the IP protocol - if it doesn't match then we drop the packet and - // do nothing with it - if bs[0]&0xf0 == 0x60 { - // Check if we have a fully-sized IPv6 header - if len(bs) < 40 { - tun.log.Traceln("TUN iface read undersized ipv6 packet, length:", len(bs)) + panic("TODO") + /* + if err != nil { + tun.log.Errorln("TUN iface read error:", err) return } - // Check the packet size - if n-tun_IPv6_HEADER_LENGTH != 256*int(bs[4])+int(bs[5]) { + // Offset the buffer from now on so that we can ignore ethernet frames if + // they are present + bs := recvd[:] + // Check if the packet is long enough to detect if it's an ICMP packet or not + if len(bs) < 7 { + tun.log.Traceln("TUN iface read undersized unknown packet, length:", len(bs)) return } - // IPv6 address - addrlen = 16 - copy(dstAddr[:addrlen], bs[24:]) - copy(dstSnet[:addrlen/2], bs[24:]) - } else if bs[0]&0xf0 == 0x40 { - // Check if we have a fully-sized IPv4 header - if len(bs) < 20 { - tun.log.Traceln("TUN iface read undersized ipv4 packet, length:", len(bs)) + // From the IP header, work out what our source and destination addresses + // and node IDs are. We will need these in order to work out where to send + // the packet + var dstAddr address.Address + var dstSnet address.Subnet + var addrlen int + n := len(bs) + // Check the IP protocol - if it doesn't match then we drop the packet and + // do nothing with it + if bs[0]&0xf0 == 0x60 { + // Check if we have a fully-sized IPv6 header + if len(bs) < 40 { + tun.log.Traceln("TUN iface read undersized ipv6 packet, length:", len(bs)) + return + } + // Check the packet size + if n-tun_IPv6_HEADER_LENGTH != 256*int(bs[4])+int(bs[5]) { + return + } + // IPv6 address + addrlen = 16 + copy(dstAddr[:addrlen], bs[24:]) + copy(dstSnet[:addrlen/2], bs[24:]) + } else if bs[0]&0xf0 == 0x40 { + // Check if we have a fully-sized IPv4 header + if len(bs) < 20 { + tun.log.Traceln("TUN iface read undersized ipv4 packet, length:", len(bs)) + return + } + // Check the packet size + if n != 256*int(bs[2])+int(bs[3]) { + return + } + // IPv4 address + addrlen = 4 + copy(dstAddr[:addrlen], bs[16:]) + } else { + // Unknown address length or protocol, so drop the packet and ignore it + tun.log.Traceln("Unknown packet type, dropping") return } - // Check the packet size - if n != 256*int(bs[2])+int(bs[3]) { - return + if tun.ckr.isEnabled() { + if addrlen != 16 || (!dstAddr.IsValid() && !dstSnet.IsValid()) { + if key, err := tun.ckr.getPublicKeyForAddress(dstAddr, addrlen); err == nil { + // A public key was found, get the node ID for the search + panic("TODO") + //dstNodeID := crypto.GetNodeID(&key) + //dstAddr = *address.AddrForNodeID(dstNodeID) + //dstSnet = *address.SubnetForNodeID(dstNodeID) + addrlen = 16 + } + } } - // IPv4 address - addrlen = 4 - copy(dstAddr[:addrlen], bs[16:]) - } else { - // Unknown address length or protocol, so drop the packet and ignore it - tun.log.Traceln("Unknown packet type, dropping") - return - } - if tun.ckr.isEnabled() { if addrlen != 16 || (!dstAddr.IsValid() && !dstSnet.IsValid()) { - if /*key*/ _, err := tun.ckr.getPublicKeyForAddress(dstAddr, addrlen); err == nil { - // A public key was found, get the node ID for the search - panic("TODO") - //dstNodeID := crypto.GetNodeID(&key) - //dstAddr = *address.AddrForNodeID(dstNodeID) - //dstSnet = *address.SubnetForNodeID(dstNodeID) - addrlen = 16 + // Couldn't find this node's ygg IP + dlen := len(bs) + if dlen > 900 { + dlen = 900 } + ptb := &icmp.DstUnreach{ + Data: bs[:dlen], + } + if packet, err := CreateICMPv6(bs[8:24], bs[24:40], ipv6.ICMPTypeDestinationUnreachable, 0, ptb); err == nil { + tun.writer.writeFrom(nil, packet) + } + return } - } - if addrlen != 16 || (!dstAddr.IsValid() && !dstSnet.IsValid()) { - // Couldn't find this node's ygg IP - dlen := len(bs) - if dlen > 900 { - dlen = 900 - } - ptb := &icmp.DstUnreach{ - Data: bs[:dlen], - } - if packet, err := CreateICMPv6(bs[8:24], bs[24:40], ipv6.ICMPTypeDestinationUnreachable, 0, ptb); err == nil { - tun.writer.writeFrom(nil, packet) - } - return - } - // Do we have an active connection for this node address? - var dstString string - session, isIn := tun.addrToConn[dstAddr] - if !isIn || session == nil { - session, isIn = tun.subnetToConn[dstSnet] + // Do we have an active connection for this node address? + var dstString string + session, isIn := tun.addrToConn[dstAddr] if !isIn || session == nil { - // Neither an address nor a subnet mapping matched, therefore populate - // the node ID and mask to commence a search - panic("TODO") - if dstAddr.IsValid() { - //dstString = dstAddr.GetNodeIDLengthString() - } else { - //dstString = dstSnet.GetNodeIDLengthString() + session, isIn = tun.subnetToConn[dstSnet] + if !isIn || session == nil { + // Neither an address nor a subnet mapping matched, therefore populate + // the node ID and mask to commence a search + panic("TODO") + if dstAddr.IsValid() { + //dstString = dstAddr.GetNodeIDLengthString() + } else { + //dstString = dstSnet.GetNodeIDLengthString() + } } } - } - // If we don't have a connection then we should open one - if !isIn || session == nil { - // Check we haven't been given empty node ID, really this shouldn't ever - // happen but just to be sure... - if dstString == "" { - panic("Given empty dstString - this shouldn't happen") + // If we don't have a connection then we should open one + if !isIn || session == nil { + // Check we haven't been given empty node ID, really this shouldn't ever + // happen but just to be sure... + if dstString == "" { + panic("Given empty dstString - this shouldn't happen") + } + _, known := tun.dials[dstString] + tun.dials[dstString] = append(tun.dials[dstString], bs) + for len(tun.dials[dstString]) > 32 { + tun.dials[dstString] = tun.dials[dstString][1:] + } + if !known { + go func() { + conn, err := tun.dialer.Dial("nodeid", dstString) + tun.Act(nil, func() { + packets := tun.dials[dstString] + delete(tun.dials, dstString) + if err != nil { + return + } + // We've been given a connection so prepare the session wrapper + var tc *tunConn + if tc, err = tun._wrap(conn.(*yggdrasil.Conn)); err != nil { + // Something went wrong when storing the connection, typically that + // something already exists for this address or subnet + tun.log.Debugln("TUN iface wrap:", err) + return + } + for _, packet := range packets { + tc.writeFrom(nil, packet) + } + }) + }() + } } - _, known := tun.dials[dstString] - tun.dials[dstString] = append(tun.dials[dstString], bs) - for len(tun.dials[dstString]) > 32 { - tun.dials[dstString] = tun.dials[dstString][1:] + // If we have a connection now, try writing to it + if isIn && session != nil { + session.writeFrom(tun, bs) } - if !known { - go func() { - conn, err := tun.dialer.Dial("nodeid", dstString) - tun.Act(nil, func() { - packets := tun.dials[dstString] - delete(tun.dials, dstString) - if err != nil { - return - } - // We've been given a connection so prepare the session wrapper - var tc *tunConn - if tc, err = tun._wrap(conn.(*yggdrasil.Conn)); err != nil { - // Something went wrong when storing the connection, typically that - // something already exists for this address or subnet - tun.log.Debugln("TUN iface wrap:", err) - return - } - for _, packet := range packets { - tc.writeFrom(nil, packet) - } - }) - }() - } - } - // If we have a connection now, try writing to it - if isIn && session != nil { - session.writeFrom(tun, bs) - } + */ } diff --git a/src/tuntap/keystore.go b/src/tuntap/keystore.go new file mode 100644 index 0000000..8901965 --- /dev/null +++ b/src/tuntap/keystore.go @@ -0,0 +1,152 @@ +package tuntap + +import ( + "crypto/ed25519" + "sync" + "time" + + iwt "github.com/Arceliar/ironwood/types" + + "github.com/yggdrasil-network/yggdrasil-go/src/address" +) + +const keyStoreTimeout = 2 * time.Minute + +type keyStore struct { + tun *TunAdapter + mutex sync.Mutex + keyToInfo map[keyArray]*keyInfo + addrToInfo map[address.Address]*keyInfo + addrBuffer map[address.Address]*buffer + subnetToInfo map[address.Subnet]*keyInfo + subnetBuffer map[address.Subnet]*buffer +} + +type keyArray [ed25519.PublicKeySize]byte + +type keyInfo struct { + key keyArray + address address.Address + subnet address.Subnet + mtu MTU // TODO use this + timeout *time.Timer // From calling a time.AfterFunc to do cleanup +} + +type buffer struct { + packets [][]byte + timeout *time.Timer +} + +func (k *keyStore) init(tun *TunAdapter) { + k.tun = tun + k.keyToInfo = make(map[keyArray]*keyInfo) + k.addrToInfo = make(map[address.Address]*keyInfo) + k.addrBuffer = make(map[address.Address]*buffer) + k.subnetToInfo = make(map[address.Subnet]*keyInfo) + k.subnetBuffer = make(map[address.Subnet]*buffer) +} + +func (k *keyStore) sendToAddress(addr address.Address, bs []byte) { + k.mutex.Lock() + defer k.mutex.Unlock() + if info := k.addrToInfo[addr]; info != nil { + k.tun.core.WriteTo(bs, iwt.Addr(info.key[:])) + k.resetTimeout(info) + } else { + var buf *buffer + if buf = k.addrBuffer[addr]; buf == nil { + buf = new(buffer) + k.addrBuffer[addr] = buf + } + msg := append([]byte(nil), bs...) + buf.packets = append(buf.packets, msg) + if buf.timeout != nil { + buf.timeout.Stop() + } + buf.timeout = time.AfterFunc(keyStoreTimeout, func() { + k.mutex.Lock() + defer k.mutex.Unlock() + if nbuf := k.addrBuffer[addr]; nbuf == buf { + delete(k.addrBuffer, addr) + } + }) + } +} + +func (k *keyStore) sendToSubnet(subnet address.Subnet, bs []byte) { + k.mutex.Lock() + defer k.mutex.Unlock() + if info := k.subnetToInfo[subnet]; info != nil { + k.tun.core.WriteTo(bs, iwt.Addr(info.key[:])) + k.resetTimeout(info) + } else { + var buf *buffer + if buf = k.subnetBuffer[subnet]; buf == nil { + buf = new(buffer) + k.subnetBuffer[subnet] = buf + } + msg := append([]byte(nil), bs...) + buf.packets = append(buf.packets, msg) + if buf.timeout != nil { + buf.timeout.Stop() + } + buf.timeout = time.AfterFunc(keyStoreTimeout, func() { + k.mutex.Lock() + defer k.mutex.Unlock() + if nbuf := k.subnetBuffer[subnet]; nbuf == buf { + delete(k.subnetBuffer, subnet) + } + }) + } +} + +func (k *keyStore) update(key ed25519.PublicKey) { + k.mutex.Lock() + defer k.mutex.Unlock() + var kArray keyArray + copy(kArray[:], key) + var info *keyInfo + if info = k.keyToInfo[kArray]; info == nil { + info = new(keyInfo) + info.key = kArray + info.address = *address.AddrForKey(ed25519.PublicKey(info.key[:])) + info.subnet = *address.SubnetForKey(ed25519.PublicKey(info.key[:])) + info.mtu = MTU(^uint16(0)) // TODO + k.keyToInfo[info.key] = info + k.addrToInfo[info.address] = info + k.subnetToInfo[info.subnet] = info + k.resetTimeout(info) + if buf := k.addrBuffer[info.address]; buf != nil { + for _, bs := range buf.packets { + k.tun.core.WriteTo(bs, iwt.Addr(info.key[:])) + } + delete(k.addrBuffer, info.address) + } + if buf := k.subnetBuffer[info.subnet]; buf != nil { + for _, bs := range buf.packets { + k.tun.core.WriteTo(bs, iwt.Addr(info.key[:])) + } + delete(k.subnetBuffer, info.subnet) + } + } + k.resetTimeout(info) +} + +func (k *keyStore) resetTimeout(info *keyInfo) { + if info.timeout != nil { + info.timeout.Stop() + } + info.timeout = time.AfterFunc(keyStoreTimeout, func() { + k.mutex.Lock() + defer k.mutex.Unlock() + if nfo := k.keyToInfo[info.key]; nfo == info { + delete(k.keyToInfo, info.key) + } + if nfo := k.addrToInfo[info.address]; nfo == info { + delete(k.addrToInfo, info.address) + } + if nfo := k.subnetToInfo[info.subnet]; nfo == info { + delete(k.subnetToInfo, info.subnet) + } + }) +} diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index 06f609a..fc1b7eb 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -9,6 +9,7 @@ package tuntap // TODO: Don't block in reader on writes that are pending searches import ( + "crypto/ed25519" "encoding/hex" "errors" "fmt" @@ -38,13 +39,12 @@ const tun_IPv6_HEADER_LENGTH = 40 // calling yggdrasil.Start(). type TunAdapter struct { core *yggdrasil.Core + store keyStore writer tunWriter reader tunReader config *config.NodeState log *log.Logger reconfigure chan chan error - listener *yggdrasil.Listener - dialer *yggdrasil.Dialer addr address.Address subnet address.Subnet ckr cryptokey @@ -53,15 +53,12 @@ type TunAdapter struct { iface tun.Device phony.Inbox // Currently only used for _handlePacket from the reader, TODO: all the stuff that currently needs a mutex below //mutex sync.RWMutex // Protects the below - addrToConn map[address.Address]*tunConn - subnetToConn map[address.Subnet]*tunConn - dials map[string][][]byte // Buffer of packets to send after dialing finishes - isOpen bool + isOpen bool } type TunOptions struct { - Listener *yggdrasil.Listener - Dialer *yggdrasil.Dialer + //Listener *yggdrasil.Listener + //Dialer *yggdrasil.Dialer } // Gets the maximum supported MTU for the platform based on the defaults in @@ -113,20 +110,20 @@ func MaximumMTU() MTU { // Init initialises the TUN 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(core *yggdrasil.Core, config *config.NodeState, log *log.Logger, options interface{}) error { + /* TODO tunoptions, ok := options.(TunOptions) if !ok { return fmt.Errorf("invalid options supplied to TunAdapter module") } + */ tun.core = core + tun.store.init(tun) tun.config = config tun.log = log - tun.listener = tunoptions.Listener - tun.dialer = tunoptions.Dialer - tun.addrToConn = make(map[address.Address]*tunConn) - tun.subnetToConn = make(map[address.Subnet]*tunConn) - tun.dials = make(map[string][][]byte) tun.writer.tun = tun tun.reader.tun = tun + tun.core.SetOutOfBandHandler(tun.oobHandler) + return nil } @@ -145,7 +142,7 @@ func (tun *TunAdapter) _start() error { return errors.New("TUN module is already started") } current := tun.config.GetCurrent() - if tun.config == nil || tun.listener == nil || tun.dialer == nil { + if tun.config == nil { return errors.New("no configuration available to TUN") } var boxPub crypto.BoxPubKey @@ -169,9 +166,9 @@ func (tun *TunAdapter) _start() error { if tun.MTU() != current.IfMTU { tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", current.IfMTU, tun.MTU(), MaximumMTU()) } - tun.core.SetMaximumSessionMTU(tun.MTU()) + // TODO tun.core.SetMaximumSessionMTU(tun.MTU()) tun.isOpen = true - go tun.handler() + // TODO go tun.handler() tun.reader.Act(nil, tun.reader._read) // Start the reader tun.ckr.init(tun) return nil @@ -225,6 +222,7 @@ func (tun *TunAdapter) UpdateConfig(config *config.NodeConfig) { tun.Act(nil, tun.ckr.configure) } +/* func (tun *TunAdapter) handler() error { for { // Accept the incoming connection @@ -283,3 +281,19 @@ func (tun *TunAdapter) _wrap(conn *yggdrasil.Conn) (c *tunConn, err error) { // Return return c, err } +*/ + +func (tun *TunAdapter) oobHandler(fromKey, toKey ed25519.PublicKey, data []byte) { + panic("TODO") + // parse packet + // If it's a lookup then send a response + // If it's a response then (maybe) update the keystore +} + +func (tun *TunAdapter) sendKeyLookup(partial ed25519.PublicKey) { + panic("TODO") +} + +func (tun *TunAdapter) sendKeyResponse(dest ed25519.PublicKey) { + panic("TODO") +}