cleaned up develop branch

This commit is contained in:
vadym 2021-09-09 10:55:30 +03:00
parent bfe42d8899
commit 2d36105eeb
17 changed files with 102 additions and 817 deletions

View file

@ -1,430 +0,0 @@
package tuntap
import (
"bytes"
"crypto/ed25519"
"encoding/hex"
"errors"
"fmt"
"net"
"sort"
"sync"
"sync/atomic"
"github.com/RiV-chain/RiV-mesh/src/address"
)
// This module implements crypto-key routing, similar to Wireguard, where we
// allow traffic for non-Mesh ranges to be routed over Mesh.
type cryptokey struct {
tun *TunAdapter
enabled atomic.Value // bool
ipv4remotes []cryptokey_route
ipv6remotes []cryptokey_route
ipv4cache map[address.Address]cryptokey_route
ipv6cache map[address.Address]cryptokey_route
ipv4locals []net.IPNet
ipv6locals []net.IPNet
mutexremotes sync.RWMutex
mutexcaches sync.RWMutex
mutexlocals sync.RWMutex
}
type cryptokey_route struct {
subnet net.IPNet
destination ed25519.PublicKey
}
// Initialise crypto-key routing. This must be done before any other CKR calls.
func (c *cryptokey) init(tun *TunAdapter) {
c.tun = tun
c.configure()
}
// Configure the CKR routes. This should only ever be ran by the TUN/TAP actor.
func (c *cryptokey) configure() {
//current := c.tun.config.GetCurrent()
// Set enabled/disabled state
c.setEnabled(c.tun.config.TunnelRouting.Enable)
// Clear out existing routes
c.mutexremotes.Lock()
c.ipv6remotes = make([]cryptokey_route, 0)
c.ipv4remotes = make([]cryptokey_route, 0)
c.mutexremotes.Unlock()
// Add IPv6 routes
for ipv6, pubkey := range c.tun.config.TunnelRouting.IPv6RemoteSubnets {
if err := c.addRemoteSubnet(ipv6, pubkey); err != nil {
c.tun.log.Errorln("Error adding CKR IPv6 remote subnet:", err)
}
}
// Add IPv4 routes
for ipv4, pubkey := range c.tun.config.TunnelRouting.IPv4RemoteSubnets {
if err := c.addRemoteSubnet(ipv4, pubkey); err != nil {
c.tun.log.Errorln("Error adding CKR IPv4 remote subnet:", err)
}
}
// Clear out existing sources
c.mutexlocals.Lock()
c.ipv6locals = make([]net.IPNet, 0)
c.ipv4locals = make([]net.IPNet, 0)
c.mutexlocals.Unlock()
// Add IPv6 sources
c.ipv6locals = make([]net.IPNet, 0)
for _, source := range c.tun.config.TunnelRouting.IPv6LocalSubnets {
if err := c.addLocalSubnet(source); err != nil {
c.tun.log.Errorln("Error adding CKR IPv6 local subnet:", err)
}
}
// Add IPv4 sources
c.ipv4locals = make([]net.IPNet, 0)
for _, source := range c.tun.config.TunnelRouting.IPv4LocalSubnets {
if err := c.addLocalSubnet(source); err != nil {
c.tun.log.Errorln("Error adding CKR IPv4 local subnet:", err)
}
}
// Wipe the caches
c.mutexcaches.Lock()
c.ipv4cache = make(map[address.Address]cryptokey_route, 0)
c.ipv6cache = make(map[address.Address]cryptokey_route, 0)
c.mutexcaches.Unlock()
}
// Enable or disable crypto-key routing.
func (c *cryptokey) setEnabled(enabled bool) {
c.enabled.Store(enabled)
}
// Check if crypto-key routing is enabled.
func (c *cryptokey) isEnabled() bool {
enabled, ok := c.enabled.Load().(bool)
return ok && enabled
}
// Check whether the given address (with the address length specified in bytes)
// matches either the current node's address, the node's routed subnet or the
// list of subnets specified in ipv4locals/ipv6locals.
func (c *cryptokey) isValidLocalAddress(addr address.Address, addrlen int) bool {
c.mutexlocals.RLock()
defer c.mutexlocals.RUnlock()
// Does it match a configured CKR source?
if c.isEnabled() {
ip := net.IP(addr[:addrlen])
// Build our references to the routing sources
var routingsources *[]net.IPNet
// Check if the prefix is IPv4 or IPv6
if addrlen == net.IPv6len {
routingsources = &c.ipv6locals
} else if addrlen == net.IPv4len {
routingsources = &c.ipv4locals
} else {
return false
}
for _, subnet := range *routingsources {
if subnet.Contains(ip) {
return true
}
}
}
// Doesn't match any of the above
return false
}
// Adds a source subnet, which allows traffic with these source addresses to
// be tunnelled using crypto-key routing.
func (c *cryptokey) addLocalSubnet(cidr string) error {
c.mutexlocals.Lock()
defer c.mutexlocals.Unlock()
// Is the CIDR we've been given valid?
_, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
return err
}
// Get the prefix length and size
_, prefixsize := ipnet.Mask.Size()
// Build our references to the routing sources
var routingsources *[]net.IPNet
// Check if the prefix is IPv4 or IPv6
if prefixsize == net.IPv6len*8 {
routingsources = &c.ipv6locals
} else if prefixsize == net.IPv4len*8 {
routingsources = &c.ipv4locals
} else {
return errors.New("unexpected prefix size")
}
// Check if we already have this CIDR
for _, subnet := range *routingsources {
if subnet.String() == ipnet.String() {
return errors.New("local subnet already configured")
}
}
// Add the source subnet
*routingsources = append(*routingsources, *ipnet)
c.tun.log.Infoln("Added CKR local subnet", cidr)
return nil
}
// Adds a destination route for the given CIDR to be tunnelled to the node
// with the given BoxPubKey.
func (c *cryptokey) addRemoteSubnet(cidr string, dest string) error {
c.mutexremotes.Lock()
c.mutexcaches.Lock()
defer c.mutexremotes.Unlock()
defer c.mutexcaches.Unlock()
// Is the CIDR we've been given valid?
ipaddr, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
return err
}
// Get the prefix length and size
_, prefixsize := ipnet.Mask.Size()
// Build our references to the routing table and cache
var routingtable *[]cryptokey_route
var routingcache *map[address.Address]cryptokey_route
// Check if the prefix is IPv4 or IPv6
if prefixsize == net.IPv6len*8 {
routingtable = &c.ipv6remotes
routingcache = &c.ipv6cache
} else if prefixsize == net.IPv4len*8 {
routingtable = &c.ipv4remotes
routingcache = &c.ipv4cache
} else {
return errors.New("unexpected prefix size")
}
// Is the route an Mesh destination?
var addr address.Address
var snet address.Subnet
copy(addr[:], ipaddr)
copy(snet[:], ipnet.IP)
if addr.IsValid() || snet.IsValid() {
return errors.New("can't specify Mesh destination as crypto-key route")
}
// Do we already have a route for this subnet?
for _, route := range *routingtable {
if route.subnet.String() == ipnet.String() {
return fmt.Errorf("remote subnet already exists for %s", cidr)
}
}
// Decode the public key
if bpk, err := hex.DecodeString(dest); err != nil {
return err
} else if len(bpk) != ed25519.PublicKeySize {
return fmt.Errorf("incorrect key length for %s", dest)
} else {
// Add the new crypto-key route
var key ed25519.PublicKey
copy(key[:], bpk)
*routingtable = append(*routingtable, cryptokey_route{
subnet: *ipnet,
destination: key,
})
// Sort so most specific routes are first
sort.Slice(*routingtable, func(i, j int) bool {
im, _ := (*routingtable)[i].subnet.Mask.Size()
jm, _ := (*routingtable)[j].subnet.Mask.Size()
return im > jm
})
// Clear the cache as this route might change future routing
// Setting an empty slice keeps the memory whereas nil invokes GC
for k := range *routingcache {
delete(*routingcache, k)
}
c.tun.log.Infoln("Added CKR remote subnet", cidr)
return nil
}
}
// Looks up the most specific route for the given address (with the address
// length specified in bytes) from the crypto-key routing table. An error is
// returned if the address is not suitable or no route was found.
func (c *cryptokey) getPublicKeyForAddress(addr address.Address, addrlen int) (ed25519.PublicKey, error) {
// Check if the address is a valid Mesh address - if so it
// is exempt from all CKR checking
if addr.IsValid() {
return ed25519.PublicKey{}, errors.New("cannot look up CKR for Mesh addresses")
}
// Build our references to the routing table and cache
var routingtable *[]cryptokey_route
var routingcache *map[address.Address]cryptokey_route
// Check if the prefix is IPv4 or IPv6
if addrlen == net.IPv6len {
routingcache = &c.ipv6cache
} else if addrlen == net.IPv4len {
routingcache = &c.ipv4cache
} else {
return ed25519.PublicKey{}, errors.New("unexpected prefix size")
}
// Check if there's a cache entry for this addr
c.mutexcaches.RLock()
if route, ok := (*routingcache)[addr]; ok {
c.mutexcaches.RUnlock()
return route.destination, nil
}
c.mutexcaches.RUnlock()
c.mutexremotes.RLock()
defer c.mutexremotes.RUnlock()
// Check if the prefix is IPv4 or IPv6
if addrlen == net.IPv6len {
routingtable = &c.ipv6remotes
} else if addrlen == net.IPv4len {
routingtable = &c.ipv4remotes
} else {
return ed25519.PublicKey{}, errors.New("unexpected prefix size")
}
// No cache was found - start by converting the address into a net.IP
ip := make(net.IP, addrlen)
copy(ip[:addrlen], addr[:])
// Check if we have a route. At this point c.ipv6remotes should be
// pre-sorted so that the most specific routes are first
for _, route := range *routingtable {
// Does this subnet match the given IP?
if route.subnet.Contains(ip) {
c.mutexcaches.Lock()
defer c.mutexcaches.Unlock()
// Check if the routing cache is above a certain size, if it is evict
// a random entry so we can make room for this one. We take advantage
// of the fact that the iteration order is random here
for k := range *routingcache {
if len(*routingcache) < 1024 {
break
}
delete(*routingcache, k)
}
// Cache the entry for future packets to get a faster lookup
(*routingcache)[addr] = route
// Return the boxPubKey
return route.destination, nil
}
}
// No route was found if we got to this point
return ed25519.PublicKey{}, fmt.Errorf("no route to %s", ip.String())
}
// Removes a source subnet, which allows traffic with these source addresses to
// be tunnelled using crypto-key routing.
func (c *cryptokey) removeLocalSubnet(cidr string) error {
c.mutexlocals.Lock()
defer c.mutexlocals.Unlock()
// Is the CIDR we've been given valid?
_, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
return err
}
// Get the prefix length and size
_, prefixsize := ipnet.Mask.Size()
// Build our references to the routing sources
var routingsources *[]net.IPNet
// Check if the prefix is IPv4 or IPv6
if prefixsize == net.IPv6len*8 {
routingsources = &c.ipv6locals
} else if prefixsize == net.IPv4len*8 {
routingsources = &c.ipv4locals
} else {
return errors.New("unexpected prefix size")
}
// Check if we already have this CIDR
for idx, subnet := range *routingsources {
if subnet.String() == ipnet.String() {
*routingsources = append((*routingsources)[:idx], (*routingsources)[idx+1:]...)
c.tun.log.Infoln("Removed CKR local subnet", cidr)
return nil
}
}
return errors.New("local subnet not found")
}
// Removes a destination route for the given CIDR to be tunnelled to the node
// with the given BoxPubKey.
func (c *cryptokey) removeRemoteSubnet(cidr string, dest string) error {
c.mutexremotes.Lock()
c.mutexcaches.Lock()
defer c.mutexremotes.Unlock()
defer c.mutexcaches.Unlock()
// Is the CIDR we've been given valid?
_, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
return err
}
// Get the prefix length and size
_, prefixsize := ipnet.Mask.Size()
// Build our references to the routing table and cache
var routingtable *[]cryptokey_route
var routingcache *map[address.Address]cryptokey_route
// Check if the prefix is IPv4 or IPv6
if prefixsize == net.IPv6len*8 {
routingtable = &c.ipv6remotes
routingcache = &c.ipv6cache
} else if prefixsize == net.IPv4len*8 {
routingtable = &c.ipv4remotes
routingcache = &c.ipv4cache
} else {
return errors.New("unexpected prefix size")
}
// Decode the public key
bpk, err := hex.DecodeString(dest)
if err != nil {
return err
} else if len(bpk) != ed25519.PrivateKeySize {
return fmt.Errorf("incorrect key length for %s", dest)
}
netStr := ipnet.String()
for idx, route := range *routingtable {
if bytes.Equal(route.destination[:], bpk) && route.subnet.String() == netStr {
*routingtable = append((*routingtable)[:idx], (*routingtable)[idx+1:]...)
for k := range *routingcache {
delete(*routingcache, k)
}
c.tun.log.Infof("Removed CKR remote subnet %s via %s\n", cidr, dest)
return nil
}
}
return fmt.Errorf("route does not exists for %s", cidr)
}

View file

@ -1,227 +0,0 @@
package tuntap
import (
"bytes"
"crypto/ed25519"
"errors"
"time"
"github.com/Arceliar/phony"
"github.com/RiV-chain/RiV-mesh/src/address"
"github.com/RiV-chain/RiV-mesh/src/util"
"github.com/RiV-chain/RiV-mesh/src/mesh"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv6"
)
const tunConnTimeout = 2 * time.Minute
type tunConn struct {
phony.Inbox
tun *TunAdapter
conn *mesh.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().(*ed25519.PublicKey) == 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 := mesh.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.(mesh.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

@ -28,7 +28,7 @@ import (
type MTU uint16
// TunAdapter represents a running TUN interface and extends the
// mesh.Adapter type. In order to use the TUN adapter with Mesh, you
// mesh.Adapter type. In order to use the TUN adapter with Yggdrasil, you
// should pass this object to the mesh.SetRouterAdapter() function before
// calling mesh.Start().
type TunAdapter struct {
@ -37,7 +37,6 @@ type TunAdapter struct {
log *log.Logger
addr address.Address
subnet address.Subnet
ckr cryptokey
mtu uint64
iface tun.Device
phony.Inbox // Currently only used for _handlePacket from the reader, TODO: all the stuff that currently needs a mutex below
@ -93,7 +92,7 @@ func MaximumMTU() uint64 {
}
// Init initialises the TUN module. You must have acquired a Listener from
// the Mesh 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(rwc *ipv6rwc.ReadWriteCloser, config *config.NodeConfig, log *log.Logger, options interface{}) error {
tun.rwc = rwc
tun.config = config
@ -141,7 +140,6 @@ func (tun *TunAdapter) _start() error {
}
tun.rwc.SetMTU(tun.MTU())
tun.isOpen = true
tun.ckr.init(tun)
tun.isEnabled = true
go tun.read()
go tun.write()