(broken state) WIP on tuntap

This commit is contained in:
Arceliar 2021-05-08 10:39:07 -05:00
parent f1c37f8440
commit 0cff56fcc1
6 changed files with 524 additions and 547 deletions

View File

@ -112,3 +112,35 @@ func SubnetForKey(publicKey ed25519.PublicKey) *Subnet {
snet[len(prefix)-1] |= 0x01 snet[len(prefix)-1] |= 0x01
return &snet 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()
}

View File

@ -1,24 +1,24 @@
package admin package admin
import ( import (
"encoding/hex" //"encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"net" "net"
"net/url" "net/url"
"os" "os"
"strconv" //"strconv"
"strings" "strings"
"time" "time"
"github.com/gologme/log" "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/config"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto" //"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/util" //"github.com/yggdrasil-network/yggdrasil-go/src/util"
"github.com/yggdrasil-network/yggdrasil-go/src/version" //"github.com/yggdrasil-network/yggdrasil-go/src/version"
"github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" "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) { func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) {
/* TODO
a.AddHandler("getSelf", []string{}, func(in Info) (Info, error) { a.AddHandler("getSelf", []string{}, func(in Info) (Info, error) {
ip := a.core.Address().String() ip := a.core.Address().String()
subnet := a.core.Subnet() subnet := a.core.Subnet()
return Info{ return Info{
"self": Info{ "self": Info{
ip: Info{ ip: Info{
"box_pub_key": a.core.EncryptionPublicKey(), // TODO"box_pub_key": a.core.EncryptionPublicKey(),
"build_name": version.BuildName(), "build_name": version.BuildName(),
"build_version": version.BuildVersion(), "build_version": version.BuildVersion(),
"coords": fmt.Sprintf("%v", a.core.Coords()), "coords": fmt.Sprintf("%v", a.core.Coords()),
@ -140,203 +141,205 @@ func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) {
return Info{"switchqueues": queues.asMap()}, nil return Info{"switchqueues": queues.asMap()}, nil
}) })
*/ */
a.AddHandler("getDHT", []string{}, func(in Info) (Info, error) { /*
dht := make(Info) a.AddHandler("getDHT", []string{}, func(in Info) (Info, error) {
for _, d := range a.core.GetDHT() { dht := make(Info)
panic("TODO") for _, d := range a.core.GetDHT() {
addr := new(address.Address) // TODO *address.AddrForNodeID(crypto.GetNodeID(&d.PublicKey)) panic("TODO")
so := net.IP(addr[:]).String() addr := new(address.Address) // TODO *address.AddrForNodeID(crypto.GetNodeID(&d.PublicKey))
dht[so] = Info{ so := net.IP(addr[:]).String()
"coords": fmt.Sprintf("%v", d.Coords), dht[so] = Info{
"last_seen": d.LastSeen.Seconds(), "coords": fmt.Sprintf("%v", d.Coords),
"box_pub_key": hex.EncodeToString(d.PublicKey[:]), "last_seen": d.LastSeen.Seconds(),
"box_pub_key": hex.EncodeToString(d.PublicKey[:]),
}
} }
} return Info{"dht": dht}, nil
return Info{"dht": dht}, nil })
}) a.AddHandler("getSessions", []string{}, func(in Info) (Info, error) {
a.AddHandler("getSessions", []string{}, func(in Info) (Info, error) { sessions := make(Info)
sessions := make(Info) for _, s := range a.core.GetSessions() {
for _, s := range a.core.GetSessions() { panic("TODO")
panic("TODO") addr := new(address.Address) //*address.AddrForNodeID(crypto.GetNodeID(&s.PublicKey))
addr := new(address.Address) //*address.AddrForNodeID(crypto.GetNodeID(&s.PublicKey)) so := net.IP(addr[:]).String()
so := net.IP(addr[:]).String() sessions[so] = Info{
sessions[so] = Info{ "coords": fmt.Sprintf("%v", s.Coords),
"coords": fmt.Sprintf("%v", s.Coords), "bytes_sent": s.BytesSent,
"bytes_sent": s.BytesSent, "bytes_recvd": s.BytesRecvd,
"bytes_recvd": s.BytesRecvd, "mtu": s.MTU,
"mtu": s.MTU, "uptime": s.Uptime.Seconds(),
"uptime": s.Uptime.Seconds(), "was_mtu_fixed": s.WasMTUFixed,
"was_mtu_fixed": s.WasMTUFixed, "box_pub_key": hex.EncodeToString(s.PublicKey[:]),
"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{ return Info{
"added": []string{ "not_added": []string{
in["uri"].(string), in["uri"].(string),
}, },
}, nil }, errors.New("Failed to add peer")
} })
return Info{ a.AddHandler("disconnectPeer", []string{"port"}, func(in Info) (Info, error) {
"not_added": []string{ port, err := strconv.ParseInt(fmt.Sprint(in["port"]), 10, 64)
in["uri"].(string), if err != nil {
}, return Info{}, err
}, errors.New("Failed to add peer") }
}) if a.core.DisconnectPeer(uint64(port)) == nil {
a.AddHandler("disconnectPeer", []string{"port"}, func(in Info) (Info, error) { return Info{
port, err := strconv.ParseInt(fmt.Sprint(in["port"]), 10, 64) "disconnected": []string{
if err != nil { fmt.Sprint(port),
return Info{}, err },
} }, nil
if a.core.DisconnectPeer(uint64(port)) == nil { } else {
return Info{ return Info{
"disconnected": []string{ "not_disconnected": []string{
fmt.Sprint(port), fmt.Sprint(port),
}, },
}, nil }, errors.New("Failed to disconnect peer")
} else { }
return Info{ })
"not_disconnected": []string{ a.AddHandler("removePeer", []string{"uri", "[interface]"}, func(in Info) (Info, error) {
fmt.Sprint(port), // Set sane defaults
}, intf := ""
}, errors.New("Failed to disconnect peer") // Has interface been specified?
} if itf, ok := in["interface"]; ok {
}) intf = itf.(string)
a.AddHandler("removePeer", []string{"uri", "[interface]"}, func(in Info) (Info, error) { }
// Set sane defaults if a.core.RemovePeer(in["uri"].(string), intf) == nil {
intf := "" return Info{
// Has interface been specified? "removed": []string{
if itf, ok := in["interface"]; ok { in["uri"].(string),
intf = itf.(string) },
} }, nil
if a.core.RemovePeer(in["uri"].(string), intf) == nil { } else {
return Info{ return Info{
"removed": []string{ "not_removed": []string{
in["uri"].(string), in["uri"].(string),
}, },
}, nil }, errors.New("Failed to remove peer")
} else { }
return Info{ return Info{
"not_removed": []string{ "not_removed": []string{
in["uri"].(string), in["uri"].(string),
}, },
}, errors.New("Failed to remove peer") }, errors.New("Failed to remove peer")
} })
return Info{ a.AddHandler("getAllowedEncryptionPublicKeys", []string{}, func(in Info) (Info, error) {
"not_removed": []string{ return Info{"allowed_box_pubs": a.core.GetAllowedEncryptionPublicKeys()}, nil
in["uri"].(string), })
}, a.AddHandler("addAllowedEncryptionPublicKey", []string{"box_pub_key"}, func(in Info) (Info, error) {
}, errors.New("Failed to remove peer") if a.core.AddAllowedEncryptionPublicKey(in["box_pub_key"].(string)) == nil {
}) return Info{
a.AddHandler("getAllowedEncryptionPublicKeys", []string{}, func(in Info) (Info, error) { "added": []string{
return Info{"allowed_box_pubs": a.core.GetAllowedEncryptionPublicKeys()}, nil in["box_pub_key"].(string),
}) },
a.AddHandler("addAllowedEncryptionPublicKey", []string{"box_pub_key"}, func(in Info) (Info, error) { }, nil
if a.core.AddAllowedEncryptionPublicKey(in["box_pub_key"].(string)) == nil { }
return Info{ return Info{
"added": []string{ "not_added": []string{
in["box_pub_key"].(string), in["box_pub_key"].(string),
}, },
}, nil }, errors.New("Failed to add allowed key")
} })
return Info{ a.AddHandler("removeAllowedEncryptionPublicKey", []string{"box_pub_key"}, func(in Info) (Info, error) {
"not_added": []string{ if a.core.RemoveAllowedEncryptionPublicKey(in["box_pub_key"].(string)) == nil {
in["box_pub_key"].(string), return Info{
}, "removed": []string{
}, errors.New("Failed to add allowed key") in["box_pub_key"].(string),
}) },
a.AddHandler("removeAllowedEncryptionPublicKey", []string{"box_pub_key"}, func(in Info) (Info, error) { }, nil
if a.core.RemoveAllowedEncryptionPublicKey(in["box_pub_key"].(string)) == nil { }
return Info{ return Info{
"removed": []string{ "not_removed": []string{
in["box_pub_key"].(string), in["box_pub_key"].(string),
}, },
}, nil }, errors.New("Failed to remove allowed key")
} })
return Info{ a.AddHandler("dhtPing", []string{"box_pub_key", "coords", "[target]"}, func(in Info) (Info, error) {
"not_removed": []string{ var reserr error
in["box_pub_key"].(string), var result yggdrasil.DHTRes
}, if in["target"] == nil {
}, errors.New("Failed to remove allowed key") in["target"] = "none"
})
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)
} }
} else { coords := util.DecodeCoordString(in["coords"].(string))
return Info{}, err var boxPubKey crypto.BoxPubKey
}
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 { if b, err := hex.DecodeString(in["box_pub_key"].(string)); err == nil {
copy(boxPubKey[:], b) 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 { } else {
return Info{}, err return Info{}, err
} }
coords = util.DecodeCoordString(in["coords"].(string)) if reserr != nil {
} return Info{}, reserr
result, err := a.core.GetNodeInfo(boxPubKey, coords, nocache) }
if err == nil { infos := make(map[string]map[string]string, len(result.Infos))
var m map[string]interface{} for _, dinfo := range result.Infos {
if err = json.Unmarshal(result, &m); err == nil { info := map[string]string{
return Info{"nodeinfo": m}, nil "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
} })
return Info{}, err */
})
} }
// Start runs the admin API socket to listen for / respond to admin API calls. // Start runs the admin API socket to listen for / respond to admin API calls.

View File

@ -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)
}

View File

@ -1,12 +1,12 @@
package tuntap package tuntap
import ( 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/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/icmp"
"golang.org/x/net/ipv6" //"golang.org/x/net/ipv6"
"github.com/Arceliar/phony" "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 // does the work of reading a packet and sending it to the correct tunConn
func (tun *TunAdapter) _handlePacket(recvd []byte, err error) { func (tun *TunAdapter) _handlePacket(recvd []byte, err error) {
if err != nil { panic("TODO")
tun.log.Errorln("TUN iface read error:", err) /*
return if err != nil {
} tun.log.Errorln("TUN iface read error:", err)
// 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))
return return
} }
// Check the packet size // Offset the buffer from now on so that we can ignore ethernet frames if
if n-tun_IPv6_HEADER_LENGTH != 256*int(bs[4])+int(bs[5]) { // 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 return
} }
// IPv6 address // From the IP header, work out what our source and destination addresses
addrlen = 16 // and node IDs are. We will need these in order to work out where to send
copy(dstAddr[:addrlen], bs[24:]) // the packet
copy(dstSnet[:addrlen/2], bs[24:]) var dstAddr address.Address
} else if bs[0]&0xf0 == 0x40 { var dstSnet address.Subnet
// Check if we have a fully-sized IPv4 header var addrlen int
if len(bs) < 20 { n := len(bs)
tun.log.Traceln("TUN iface read undersized ipv4 packet, length:", 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 return
} }
// Check the packet size if tun.ckr.isEnabled() {
if n != 256*int(bs[2])+int(bs[3]) { if addrlen != 16 || (!dstAddr.IsValid() && !dstSnet.IsValid()) {
return 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 addrlen != 16 || (!dstAddr.IsValid() && !dstSnet.IsValid()) {
if /*key*/ _, err := tun.ckr.getPublicKeyForAddress(dstAddr, addrlen); err == nil { // Couldn't find this node's ygg IP
// A public key was found, get the node ID for the search dlen := len(bs)
panic("TODO") if dlen > 900 {
//dstNodeID := crypto.GetNodeID(&key) dlen = 900
//dstAddr = *address.AddrForNodeID(dstNodeID)
//dstSnet = *address.SubnetForNodeID(dstNodeID)
addrlen = 16
} }
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?
if addrlen != 16 || (!dstAddr.IsValid() && !dstSnet.IsValid()) { var dstString string
// Couldn't find this node's ygg IP session, isIn := tun.addrToConn[dstAddr]
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]
if !isIn || session == nil { if !isIn || session == nil {
// Neither an address nor a subnet mapping matched, therefore populate session, isIn = tun.subnetToConn[dstSnet]
// the node ID and mask to commence a search if !isIn || session == nil {
panic("TODO") // Neither an address nor a subnet mapping matched, therefore populate
if dstAddr.IsValid() { // the node ID and mask to commence a search
//dstString = dstAddr.GetNodeIDLengthString() panic("TODO")
} else { if dstAddr.IsValid() {
//dstString = dstSnet.GetNodeIDLengthString() //dstString = dstAddr.GetNodeIDLengthString()
} else {
//dstString = dstSnet.GetNodeIDLengthString()
}
} }
} }
} // If we don't have a connection then we should open one
// If we don't have a connection then we should open one if !isIn || session == nil {
if !isIn || session == nil { // Check we haven't been given empty node ID, really this shouldn't ever
// Check we haven't been given empty node ID, really this shouldn't ever // happen but just to be sure...
// happen but just to be sure... if dstString == "" {
if dstString == "" { panic("Given empty dstString - this shouldn't happen")
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] // If we have a connection now, try writing to it
tun.dials[dstString] = append(tun.dials[dstString], bs) if isIn && session != nil {
for len(tun.dials[dstString]) > 32 { session.writeFrom(tun, bs)
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)
}
})
}()
}
}
// If we have a connection now, try writing to it
if isIn && session != nil {
session.writeFrom(tun, bs)
}
} }

152
src/tuntap/keystore.go Normal file
View File

@ -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)
}
})
}

View File

@ -9,6 +9,7 @@ package tuntap
// TODO: Don't block in reader on writes that are pending searches // TODO: Don't block in reader on writes that are pending searches
import ( import (
"crypto/ed25519"
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
@ -38,13 +39,12 @@ const tun_IPv6_HEADER_LENGTH = 40
// calling yggdrasil.Start(). // calling yggdrasil.Start().
type TunAdapter struct { type TunAdapter struct {
core *yggdrasil.Core core *yggdrasil.Core
store keyStore
writer tunWriter writer tunWriter
reader tunReader reader tunReader
config *config.NodeState config *config.NodeState
log *log.Logger log *log.Logger
reconfigure chan chan error reconfigure chan chan error
listener *yggdrasil.Listener
dialer *yggdrasil.Dialer
addr address.Address addr address.Address
subnet address.Subnet subnet address.Subnet
ckr cryptokey ckr cryptokey
@ -53,15 +53,12 @@ type TunAdapter struct {
iface tun.Device iface tun.Device
phony.Inbox // Currently only used for _handlePacket from the reader, TODO: all the stuff that currently needs a mutex below 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 //mutex sync.RWMutex // Protects the below
addrToConn map[address.Address]*tunConn isOpen bool
subnetToConn map[address.Subnet]*tunConn
dials map[string][][]byte // Buffer of packets to send after dialing finishes
isOpen bool
} }
type TunOptions struct { type TunOptions struct {
Listener *yggdrasil.Listener //Listener *yggdrasil.Listener
Dialer *yggdrasil.Dialer //Dialer *yggdrasil.Dialer
} }
// Gets the maximum supported MTU for the platform based on the defaults in // 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 // 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. // 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 { func (tun *TunAdapter) Init(core *yggdrasil.Core, config *config.NodeState, log *log.Logger, options interface{}) error {
/* TODO
tunoptions, ok := options.(TunOptions) tunoptions, ok := options.(TunOptions)
if !ok { if !ok {
return fmt.Errorf("invalid options supplied to TunAdapter module") return fmt.Errorf("invalid options supplied to TunAdapter module")
} }
*/
tun.core = core tun.core = core
tun.store.init(tun)
tun.config = config tun.config = config
tun.log = log 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.writer.tun = tun
tun.reader.tun = tun tun.reader.tun = tun
tun.core.SetOutOfBandHandler(tun.oobHandler)
return nil return nil
} }
@ -145,7 +142,7 @@ func (tun *TunAdapter) _start() error {
return errors.New("TUN module is already started") return errors.New("TUN module is already started")
} }
current := tun.config.GetCurrent() 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") return errors.New("no configuration available to TUN")
} }
var boxPub crypto.BoxPubKey var boxPub crypto.BoxPubKey
@ -169,9 +166,9 @@ func (tun *TunAdapter) _start() error {
if tun.MTU() != current.IfMTU { 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.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 tun.isOpen = true
go tun.handler() // TODO go tun.handler()
tun.reader.Act(nil, tun.reader._read) // Start the reader tun.reader.Act(nil, tun.reader._read) // Start the reader
tun.ckr.init(tun) tun.ckr.init(tun)
return nil return nil
@ -225,6 +222,7 @@ func (tun *TunAdapter) UpdateConfig(config *config.NodeConfig) {
tun.Act(nil, tun.ckr.configure) tun.Act(nil, tun.ckr.configure)
} }
/*
func (tun *TunAdapter) handler() error { func (tun *TunAdapter) handler() error {
for { for {
// Accept the incoming connection // Accept the incoming connection
@ -283,3 +281,19 @@ func (tun *TunAdapter) _wrap(conn *yggdrasil.Conn) (c *tunConn, err error) {
// Return // Return
return c, err 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")
}