fix merge conflicts

This commit is contained in:
Arceliar 2018-12-15 17:57:36 -06:00
commit 89f9f5defb
21 changed files with 463 additions and 116 deletions

View File

@ -12,6 +12,7 @@ import (
"os" "os"
"os/signal" "os/signal"
"regexp" "regexp"
"strings"
"syscall" "syscall"
"time" "time"
@ -188,6 +189,35 @@ func main() {
} }
} }
} }
// Check to see if the peers are in a parsable format, if not then default
// them to the TCP scheme
if peers, ok := dat["Peers"].([]interface{}); ok {
for index, peer := range peers {
uri := peer.(string)
if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") {
continue
}
if strings.HasPrefix(uri, "tcp:") {
uri = uri[4:]
}
(dat["Peers"].([]interface{}))[index] = "tcp://" + uri
}
}
// Now do the same with the interface peers
if interfacepeers, ok := dat["InterfacePeers"].(map[string]interface{}); ok {
for intf, peers := range interfacepeers {
for index, peer := range peers.([]interface{}) {
uri := peer.(string)
if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") {
continue
}
if strings.HasPrefix(uri, "tcp:") {
uri = uri[4:]
}
((dat["InterfacePeers"].(map[string]interface{}))[intf]).([]interface{})[index] = "tcp://" + uri
}
}
}
// Overlay our newly mapped configuration onto the autoconf node config that // Overlay our newly mapped configuration onto the autoconf node config that
// we generated above. // we generated above.
if err = mapstructure.Decode(dat, &cfg); err != nil { if err = mapstructure.Decode(dat, &cfg); err != nil {

View File

@ -87,6 +87,7 @@ func main() {
logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen) logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen)
} }
} else { } else {
endpoint = *server
logger.Println("Using endpoint", endpoint, "from command line") logger.Println("Using endpoint", endpoint, "from command line")
} }

View File

@ -71,29 +71,39 @@ etc/systemd/system/*.service etc/systemd/system
EOF EOF
cat > /tmp/$PKGNAME/debian/postinst << EOF cat > /tmp/$PKGNAME/debian/postinst << EOF
#!/bin/sh #!/bin/sh
if ! getent group yggdrasil 2>&1 > /dev/null; then
addgroup --system --quiet yggdrasil
fi
if [ -f /etc/yggdrasil.conf ]; if [ -f /etc/yggdrasil.conf ];
then then
mkdir -p /var/backups mkdir -p /var/backups
echo "Backing up configuration file to /var/backups/yggdrasil.conf.`date +%Y%m%d`" echo "Backing up configuration file to /var/backups/yggdrasil.conf.`date +%Y%m%d`"
cp /etc/yggdrasil.conf /var/backups/yggdrasil.conf.`date +%Y%m%d` cp /etc/yggdrasil.conf /var/backups/yggdrasil.conf.`date +%Y%m%d`
echo "Normalising /etc/yggdrasil.conf" echo "Normalising and updating /etc/yggdrasil.conf"
/usr/bin/yggdrasil -useconffile /var/backups/yggdrasil.conf.`date +%Y%m%d` -normaliseconf > /etc/yggdrasil.conf /usr/bin/yggdrasil -useconffile /var/backups/yggdrasil.conf.`date +%Y%m%d` -normaliseconf > /etc/yggdrasil.conf
fi chgrp yggdrasil /etc/yggdrasil.conf
if command -v systemctl >/dev/null; then if command -v systemctl >/dev/null; then
systemctl daemon-reload >/dev/null || true systemctl daemon-reload >/dev/null || true
if [ -f /etc/systemd/system/multi-user.target.wants/yggdrasil.service ]; then systemctl enable yggdrasil || true
echo "Starting Yggdrasil"
systemctl start yggdrasil || true systemctl start yggdrasil || true
fi fi
else
echo "Generating initial configuration file /etc/yggdrasil.conf"
echo "Please familiarise yourself with this file before starting Yggdrasil"
/usr/bin/yggdrasil -genconf > /etc/yggdrasil.conf
chgrp yggdrasil /etc/yggdrasil.conf
fi fi
EOF EOF
cat > /tmp/$PKGNAME/debian/prerm << EOF cat > /tmp/$PKGNAME/debian/prerm << EOF
#!/bin/sh #!/bin/sh
if command -v systemctl >/dev/null; then if command -v systemctl >/dev/null; then
if systemctl is-active --quiet yggdrasil; then if systemctl is-active --quiet yggdrasil; then
echo "Stopping Yggdrasil"
systemctl stop yggdrasil || true systemctl stop yggdrasil || true
fi fi
systemctl disable yggdrasil || true
fi fi
EOF EOF

View File

@ -4,6 +4,7 @@ Wants=network.target
After=network.target After=network.target
[Service] [Service]
Group=yggdrasil
ProtectHome=true ProtectHome=true
ProtectSystem=true ProtectSystem=true
SyslogIdentifier=yggdrasil SyslogIdentifier=yggdrasil
@ -12,7 +13,7 @@ ExecStartPre=/bin/sh -ec "if ! test -s /etc/yggdrasil.conf; \
yggdrasil -genconf > /etc/yggdrasil.conf; \ yggdrasil -genconf > /etc/yggdrasil.conf; \
echo 'WARNING: A new /etc/yggdrasil.conf file has been generated.'; \ echo 'WARNING: A new /etc/yggdrasil.conf file has been generated.'; \
fi" fi"
ExecStart=/bin/sh -c "exec yggdrasil -useconf < /etc/yggdrasil.conf" ExecStart=/usr/bin/yggdrasil -useconffile /etc/yggdrasil.conf
Restart=always Restart=always
[Install] [Install]

View File

@ -19,6 +19,7 @@ type NodeConfig struct {
SessionFirewall SessionFirewall `comment:"The session firewall controls who can send/receive network traffic\nto/from. This is useful if you want to protect this node without\nresorting to using a real firewall. This does not affect traffic\nbeing routed via this node to somewhere else. Rules are prioritised as\nfollows: blacklist, whitelist, always allow outgoing, direct, remote."` SessionFirewall SessionFirewall `comment:"The session firewall controls who can send/receive network traffic\nto/from. This is useful if you want to protect this node without\nresorting to using a real firewall. This does not affect traffic\nbeing routed via this node to somewhere else. Rules are prioritised as\nfollows: blacklist, whitelist, always allow outgoing, direct, remote."`
TunnelRouting TunnelRouting `comment:"Allow tunneling non-Yggdrasil traffic over Yggdrasil. This effectively\nallows you to use Yggdrasil to route to, or to bridge other networks,\nsimilar to a VPN tunnel. Tunnelling works between any two nodes and\ndoes not require them to be directly peered."` TunnelRouting TunnelRouting `comment:"Allow tunneling non-Yggdrasil traffic over Yggdrasil. This effectively\nallows you to use Yggdrasil to route to, or to bridge other networks,\nsimilar to a VPN tunnel. Tunnelling works between any two nodes and\ndoes not require them to be directly peered."`
SwitchOptions SwitchOptions `comment:"Advanced options for tuning the switch. Normally you will not need\nto edit these options."` SwitchOptions SwitchOptions `comment:"Advanced options for tuning the switch. Normally you will not need\nto edit these options."`
NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."`
//Net NetConfig `comment:"Extended options for connecting to peers over other networks."` //Net NetConfig `comment:"Extended options for connecting to peers over other networks."`
} }

25
src/yggdrasil/adapter.go Normal file
View File

@ -0,0 +1,25 @@
package yggdrasil
// Defines the minimum required functions for an adapter type.
type AdapterInterface interface {
init(core *Core, send chan<- []byte, recv <-chan []byte)
read() error
write() error
close() error
}
// Defines the minimum required struct members for an adapter type (this is
// now the base type for tunAdapter in tun.go)
type Adapter struct {
AdapterInterface
core *Core
send chan<- []byte
recv <-chan []byte
}
// Initialises the adapter.
func (adapter *Adapter) init(core *Core, send chan<- []byte, recv <-chan []byte) {
adapter.core = core
adapter.send = send
adapter.recv = recv
}

View File

@ -160,9 +160,9 @@ func (a *admin) init(c *Core, listenaddr string) {
}() }()
return admin_info{ return admin_info{
a.core.tun.iface.Name(): admin_info{ a.core.router.tun.iface.Name(): admin_info{
"tap_mode": a.core.tun.iface.IsTAP(), "tap_mode": a.core.router.tun.iface.IsTAP(),
"mtu": a.core.tun.mtu, "mtu": a.core.router.tun.mtu,
}, },
}, nil }, nil
}) })
@ -185,8 +185,8 @@ func (a *admin) init(c *Core, listenaddr string) {
return admin_info{}, errors.New("Failed to configure adapter") return admin_info{}, errors.New("Failed to configure adapter")
} else { } else {
return admin_info{ return admin_info{
a.core.tun.iface.Name(): admin_info{ a.core.router.tun.iface.Name(): admin_info{
"tap_mode": a.core.tun.iface.IsTAP(), "tap_mode": a.core.router.tun.iface.IsTAP(),
"mtu": ifmtu, "mtu": ifmtu,
}, },
}, nil }, nil
@ -322,6 +322,23 @@ func (a *admin) init(c *Core, listenaddr string) {
return admin_info{}, err return admin_info{}, err
} }
}) })
a.addHandler("getNodeInfo", []string{"box_pub_key", "coords", "[nocache]"}, func(in admin_info) (admin_info, error) {
var nocache bool
if in["nocache"] != nil {
nocache = in["nocache"].(string) == "true"
}
result, err := a.admin_getNodeInfo(in["box_pub_key"].(string), in["coords"].(string), nocache)
if err == nil {
var m map[string]interface{}
if err = json.Unmarshal(result, &m); err == nil {
return admin_info{"nodeinfo": m}, nil
} else {
return admin_info{}, err
}
} else {
return admin_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.
@ -515,13 +532,7 @@ func (a *admin) addPeer(addr string, sintf string) error {
return errors.New("invalid peer: " + addr) return errors.New("invalid peer: " + addr)
} }
} else { } else {
// no url scheme provided return errors.New("invalid peer: " + addr)
addr = strings.ToLower(addr)
if strings.HasPrefix(addr, "tcp:") {
addr = addr[4:]
}
a.core.tcp.connect(addr, "")
return nil
} }
return nil return nil
} }
@ -539,12 +550,12 @@ func (a *admin) removePeer(p string) error {
// startTunWithMTU creates the tun/tap device, sets its address, and sets the MTU to the provided value. // startTunWithMTU creates the tun/tap device, sets its address, and sets the MTU to the provided value.
func (a *admin) startTunWithMTU(ifname string, iftapmode bool, ifmtu int) error { func (a *admin) startTunWithMTU(ifname string, iftapmode bool, ifmtu int) error {
// Close the TUN first if open // Close the TUN first if open
_ = a.core.tun.close() _ = a.core.router.tun.close()
// Then reconfigure and start it // Then reconfigure and start it
addr := a.core.router.addr addr := a.core.router.addr
straddr := fmt.Sprintf("%s/%v", net.IP(addr[:]).String(), 8*len(address_prefix)-1) straddr := fmt.Sprintf("%s/%v", net.IP(addr[:]).String(), 8*len(address_prefix)-1)
if ifname != "none" { if ifname != "none" {
err := a.core.tun.setup(ifname, iftapmode, straddr, ifmtu) err := a.core.router.tun.setup(ifname, iftapmode, straddr, ifmtu)
if err != nil { if err != nil {
return err return err
} }
@ -559,9 +570,9 @@ func (a *admin) startTunWithMTU(ifname string, iftapmode bool, ifmtu int) error
a.core.sessions.sendPingPong(sinfo, false) a.core.sessions.sendPingPong(sinfo, false)
} }
// Aaaaand... go! // Aaaaand... go!
go a.core.tun.read() go a.core.router.tun.read()
} }
go a.core.tun.write() go a.core.router.tun.write()
return nil return nil
} }
@ -806,6 +817,52 @@ func (a *admin) admin_dhtPing(keyString, coordString, targetString string) (dhtR
return dhtRes{}, errors.New(fmt.Sprintf("DHT ping timeout: %s", keyString)) return dhtRes{}, errors.New(fmt.Sprintf("DHT ping timeout: %s", keyString))
} }
func (a *admin) admin_getNodeInfo(keyString, coordString string, nocache bool) (nodeinfoPayload, error) {
var key boxPubKey
if keyBytes, err := hex.DecodeString(keyString); err != nil {
return nodeinfoPayload{}, err
} else {
copy(key[:], keyBytes)
}
if !nocache {
if response, err := a.core.nodeinfo.getCachedNodeInfo(key); err == nil {
return response, nil
}
}
var coords []byte
for _, cstr := range strings.Split(strings.Trim(coordString, "[]"), " ") {
if cstr == "" {
// Special case, happens if trimmed is the empty string, e.g. this is the root
continue
}
if u64, err := strconv.ParseUint(cstr, 10, 8); err != nil {
return nodeinfoPayload{}, err
} else {
coords = append(coords, uint8(u64))
}
}
response := make(chan *nodeinfoPayload, 1)
sendNodeInfoRequest := func() {
a.core.nodeinfo.addCallback(key, func(nodeinfo *nodeinfoPayload) {
defer func() { recover() }()
select {
case response <- nodeinfo:
default:
}
})
a.core.nodeinfo.sendNodeInfo(key, coords, false)
}
a.core.router.doAdmin(sendNodeInfoRequest)
go func() {
time.Sleep(6 * time.Second)
close(response)
}()
for res := range response {
return *res, nil
}
return nodeinfoPayload{}, errors.New(fmt.Sprintf("getNodeInfo timeout: %s", keyString))
}
// getResponse_dot returns a response for a graphviz dot formatted representation of the known parts of the network. // getResponse_dot returns a response for a graphviz dot formatted representation of the known parts of the network.
// This is color-coded and labeled, and includes the self node, switch peers, nodes known to the DHT, and nodes with open sessions. // This is color-coded and labeled, and includes the self node, switch peers, nodes known to the DHT, and nodes with open sessions.
// The graph is structured as a tree with directed links leading away from the root. // The graph is structured as a tree with directed links leading away from the root.

View File

@ -28,10 +28,10 @@ type Core struct {
sessions sessions sessions sessions
router router router router
dht dht dht dht
tun tunDevice
admin admin admin admin
searches searches searches searches
multicast multicast multicast multicast
nodeinfo nodeinfo
tcp tcpInterface tcp tcpInterface
log *log.Logger log *log.Logger
ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this
@ -59,7 +59,6 @@ func (c *Core) init(bpub *boxPubKey,
c.peers.init(c) c.peers.init(c)
c.router.init(c) c.router.init(c)
c.switchTable.init(c, c.sigPub) // TODO move before peers? before router? c.switchTable.init(c, c.sigPub) // TODO move before peers? before router?
c.tun.init(c)
} }
// Get the current build name. This is usually injected if built from git, // Get the current build name. This is usually injected if built from git,
@ -124,6 +123,9 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
c.init(&boxPub, &boxPriv, &sigPub, &sigPriv) c.init(&boxPub, &boxPriv, &sigPub, &sigPriv)
c.admin.init(c, nc.AdminListen) c.admin.init(c, nc.AdminListen)
c.nodeinfo.init(c)
c.nodeinfo.setNodeInfo(nc.NodeInfo)
if err := c.tcp.init(c, nc.Listen, nc.ReadTimeout); err != nil { if err := c.tcp.init(c, nc.Listen, nc.ReadTimeout); err != nil {
c.log.Println("Failed to start TCP interface") c.log.Println("Failed to start TCP interface")
return err return err
@ -188,7 +190,7 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
} }
ip := net.IP(c.router.addr[:]).String() ip := net.IP(c.router.addr[:]).String()
if err := c.tun.start(nc.IfName, nc.IfTAPMode, fmt.Sprintf("%s/%d", ip, 8*len(address_prefix)-1), nc.IfMTU); err != nil { if err := c.router.tun.start(nc.IfName, nc.IfTAPMode, fmt.Sprintf("%s/%d", ip, 8*len(address_prefix)-1), nc.IfMTU); err != nil {
c.log.Println("Failed to start TUN/TAP") c.log.Println("Failed to start TUN/TAP")
return err return err
} }
@ -200,7 +202,7 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
// Stops the Yggdrasil node. // Stops the Yggdrasil node.
func (c *Core) Stop() { func (c *Core) Stop() {
c.log.Println("Stopping...") c.log.Println("Stopping...")
c.tun.close() c.router.tun.close()
c.admin.close() c.admin.close()
} }
@ -239,6 +241,16 @@ func (c *Core) GetSubnet() *net.IPNet {
return &net.IPNet{IP: subnet, Mask: net.CIDRMask(64, 128)} return &net.IPNet{IP: subnet, Mask: net.CIDRMask(64, 128)}
} }
// Gets the nodeinfo.
func (c *Core) GetNodeInfo() nodeinfoPayload {
return c.nodeinfo.getNodeInfo()
}
// Sets the nodeinfo.
func (c *Core) SetNodeInfo(nodeinfo interface{}) {
c.nodeinfo.setNodeInfo(nodeinfo)
}
// Sets the output logger of the Yggdrasil node after startup. This may be // Sets the output logger of the Yggdrasil node after startup. This may be
// useful if you want to redirect the output later. // useful if you want to redirect the output later.
func (c *Core) SetLogger(log *log.Logger) { func (c *Core) SetLogger(log *log.Logger) {
@ -293,10 +305,10 @@ func (c *Core) GetTUNDefaultIfTAPMode() bool {
// Gets the current TUN/TAP interface name. // Gets the current TUN/TAP interface name.
func (c *Core) GetTUNIfName() string { func (c *Core) GetTUNIfName() string {
return c.tun.iface.Name() return c.router.tun.iface.Name()
} }
// Gets the current TUN/TAP interface MTU. // Gets the current TUN/TAP interface MTU.
func (c *Core) GetTUNIfMTU() int { func (c *Core) GetTUNIfMTU() int {
return c.tun.mtu return c.router.tun.mtu
} }

View File

@ -68,11 +68,11 @@ func (c *Core) DEBUG_getEncryptionPublicKey() boxPubKey {
} }
func (c *Core) DEBUG_getSend() chan<- []byte { func (c *Core) DEBUG_getSend() chan<- []byte {
return c.tun.send return c.router.tun.send
} }
func (c *Core) DEBUG_getRecv() <-chan []byte { func (c *Core) DEBUG_getRecv() <-chan []byte {
return c.tun.recv return c.router.tun.recv
} }
// Peer // Peer
@ -304,18 +304,18 @@ func (c *Core) DEBUG_startTunWithMTU(ifname string, iftapmode bool, mtu int) {
addr := c.DEBUG_getAddr() addr := c.DEBUG_getAddr()
straddr := fmt.Sprintf("%s/%v", net.IP(addr[:]).String(), 8*len(address_prefix)) straddr := fmt.Sprintf("%s/%v", net.IP(addr[:]).String(), 8*len(address_prefix))
if ifname != "none" { if ifname != "none" {
err := c.tun.setup(ifname, iftapmode, straddr, mtu) err := c.router.tun.setup(ifname, iftapmode, straddr, mtu)
if err != nil { if err != nil {
panic(err) panic(err)
} }
c.log.Println("Setup TUN/TAP:", c.tun.iface.Name(), straddr) c.log.Println("Setup TUN/TAP:", c.router.tun.iface.Name(), straddr)
go func() { panic(c.tun.read()) }() go func() { panic(c.router.tun.read()) }()
} }
go func() { panic(c.tun.write()) }() go func() { panic(c.router.tun.write()) }()
} }
func (c *Core) DEBUG_stopTun() { func (c *Core) DEBUG_stopTun() {
c.tun.close() c.router.tun.close()
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -546,7 +546,7 @@ func DEBUG_simLinkPeers(p, q *peer) {
} }
func (c *Core) DEBUG_simFixMTU() { func (c *Core) DEBUG_simFixMTU() {
c.tun.mtu = 65535 c.router.tun.mtu = 65535
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -24,7 +24,7 @@ type macAddress [6]byte
const len_ETHER = 14 const len_ETHER = 14
type icmpv6 struct { type icmpv6 struct {
tun *tunDevice tun *tunAdapter
mylladdr net.IP mylladdr net.IP
mymac macAddress mymac macAddress
peermacs map[address]neighbor peermacs map[address]neighbor
@ -57,7 +57,7 @@ func ipv6Header_Marshal(h *ipv6.Header) ([]byte, error) {
// Initialises the ICMPv6 module by assigning our link-local IPv6 address and // Initialises the ICMPv6 module by assigning our link-local IPv6 address and
// our MAC address. ICMPv6 messages will always appear to originate from these // our MAC address. ICMPv6 messages will always appear to originate from these
// addresses. // addresses.
func (i *icmpv6) init(t *tunDevice) { func (i *icmpv6) init(t *tunAdapter) {
i.tun = t i.tun = t
i.peermacs = make(map[address]neighbor) i.peermacs = make(map[address]neighbor)

175
src/yggdrasil/nodeinfo.go Normal file
View File

@ -0,0 +1,175 @@
package yggdrasil
import (
"encoding/json"
"errors"
"runtime"
"sync"
"time"
)
type nodeinfo struct {
core *Core
myNodeInfo nodeinfoPayload
myNodeInfoMutex sync.RWMutex
callbacks map[boxPubKey]nodeinfoCallback
callbacksMutex sync.Mutex
cache map[boxPubKey]nodeinfoCached
cacheMutex sync.RWMutex
}
type nodeinfoPayload []byte
type nodeinfoCached struct {
payload nodeinfoPayload
created time.Time
}
type nodeinfoCallback struct {
call func(nodeinfo *nodeinfoPayload)
created time.Time
}
// Represents a session nodeinfo packet.
type nodeinfoReqRes struct {
SendPermPub boxPubKey // Sender's permanent key
SendCoords []byte // Sender's coords
IsResponse bool
NodeInfo nodeinfoPayload
}
// Initialises the nodeinfo cache/callback maps, and starts a goroutine to keep
// the cache/callback maps clean of stale entries
func (m *nodeinfo) init(core *Core) {
m.core = core
m.callbacks = make(map[boxPubKey]nodeinfoCallback)
m.cache = make(map[boxPubKey]nodeinfoCached)
go func() {
for {
m.callbacksMutex.Lock()
for boxPubKey, callback := range m.callbacks {
if time.Since(callback.created) > time.Minute {
delete(m.callbacks, boxPubKey)
}
}
m.callbacksMutex.Unlock()
m.cacheMutex.Lock()
for boxPubKey, cache := range m.cache {
if time.Since(cache.created) > time.Hour {
delete(m.cache, boxPubKey)
}
}
m.cacheMutex.Unlock()
time.Sleep(time.Second * 30)
}
}()
}
// Add a callback for a nodeinfo lookup
func (m *nodeinfo) addCallback(sender boxPubKey, call func(nodeinfo *nodeinfoPayload)) {
m.callbacksMutex.Lock()
defer m.callbacksMutex.Unlock()
m.callbacks[sender] = nodeinfoCallback{
created: time.Now(),
call: call,
}
}
// Handles the callback, if there is one
func (m *nodeinfo) callback(sender boxPubKey, nodeinfo nodeinfoPayload) {
m.callbacksMutex.Lock()
defer m.callbacksMutex.Unlock()
if callback, ok := m.callbacks[sender]; ok {
callback.call(&nodeinfo)
delete(m.callbacks, sender)
}
}
// Get the current node's nodeinfo
func (m *nodeinfo) getNodeInfo() nodeinfoPayload {
m.myNodeInfoMutex.RLock()
defer m.myNodeInfoMutex.RUnlock()
return m.myNodeInfo
}
// Set the current node's nodeinfo
func (m *nodeinfo) setNodeInfo(given interface{}) error {
m.myNodeInfoMutex.Lock()
defer m.myNodeInfoMutex.Unlock()
newnodeinfo := map[string]interface{}{
"buildname": GetBuildName(),
"buildversion": GetBuildVersion(),
"buildplatform": runtime.GOOS,
"buildarch": runtime.GOARCH,
}
if nodeinfomap, ok := given.(map[string]interface{}); ok {
for key, value := range nodeinfomap {
if _, ok := newnodeinfo[key]; ok {
continue
}
newnodeinfo[key] = value
}
}
if newjson, err := json.Marshal(newnodeinfo); err == nil {
if len(newjson) > 16384 {
return errors.New("NodeInfo exceeds max length of 16384 bytes")
}
m.myNodeInfo = newjson
return nil
} else {
return err
}
}
// Add nodeinfo into the cache for a node
func (m *nodeinfo) addCachedNodeInfo(key boxPubKey, payload nodeinfoPayload) {
m.cacheMutex.Lock()
defer m.cacheMutex.Unlock()
m.cache[key] = nodeinfoCached{
created: time.Now(),
payload: payload,
}
}
// Get a nodeinfo entry from the cache
func (m *nodeinfo) getCachedNodeInfo(key boxPubKey) (nodeinfoPayload, error) {
m.cacheMutex.RLock()
defer m.cacheMutex.RUnlock()
if nodeinfo, ok := m.cache[key]; ok {
return nodeinfo.payload, nil
}
return nodeinfoPayload{}, errors.New("No cache entry found")
}
// Handles a nodeinfo request/response - called from the router
func (m *nodeinfo) handleNodeInfo(nodeinfo *nodeinfoReqRes) {
if nodeinfo.IsResponse {
m.callback(nodeinfo.SendPermPub, nodeinfo.NodeInfo)
m.addCachedNodeInfo(nodeinfo.SendPermPub, nodeinfo.NodeInfo)
} else {
m.sendNodeInfo(nodeinfo.SendPermPub, nodeinfo.SendCoords, true)
}
}
// Send nodeinfo request or response - called from the router
func (m *nodeinfo) sendNodeInfo(key boxPubKey, coords []byte, isResponse bool) {
table := m.core.switchTable.table.Load().(lookupTable)
nodeinfo := nodeinfoReqRes{
SendCoords: table.self.getCoords(),
IsResponse: isResponse,
NodeInfo: m.core.nodeinfo.getNodeInfo(),
}
bs := nodeinfo.encode()
shared := m.core.sessions.getSharedKey(&m.core.boxPriv, &key)
payload, nonce := boxSeal(shared, bs, nil)
p := wire_protoTrafficPacket{
Coords: coords,
ToKey: key,
FromKey: m.core.boxPub,
Nonce: *nonce,
Payload: payload,
}
packet := p.encode()
m.core.router.out(packet)
}

View File

@ -86,7 +86,6 @@ type peer struct {
shared boxSharedKey shared boxSharedKey
linkShared boxSharedKey linkShared boxSharedKey
endpoint string endpoint string
friendlyName string
firstSeen time.Time // To track uptime for getPeers firstSeen time.Time // To track uptime for getPeers
linkOut (chan []byte) // used for protocol traffic (to bypass queues) linkOut (chan []byte) // used for protocol traffic (to bypass queues)
doSend (chan struct{}) // tell the linkLoop to send a switchMsg doSend (chan struct{}) // tell the linkLoop to send a switchMsg

View File

@ -39,6 +39,8 @@ type router struct {
in <-chan []byte // packets we received from the network, link to peer's "out" in <-chan []byte // packets we received from the network, link to peer's "out"
out func([]byte) // packets we're sending to the network, link to peer's "in" out func([]byte) // packets we're sending to the network, link to peer's "in"
toRecv chan router_recvPacket // packets to handle via recvPacket() toRecv chan router_recvPacket // packets to handle via recvPacket()
tun tunAdapter // TUN/TAP adapter
adapters []Adapter // Other adapters
recv chan<- []byte // place where the tun pulls received packets from recv chan<- []byte // place where the tun pulls received packets from
send <-chan []byte // place where the tun puts outgoing packets send <-chan []byte // place where the tun puts outgoing packets
reset chan struct{} // signal that coords changed (re-init sessions/dht) reset chan struct{} // signal that coords changed (re-init sessions/dht)
@ -75,12 +77,10 @@ func (r *router) init(core *Core) {
send := make(chan []byte, 32) send := make(chan []byte, 32)
r.recv = recv r.recv = recv
r.send = send r.send = send
r.core.tun.recv = recv
r.core.tun.send = send
r.reset = make(chan struct{}, 1) r.reset = make(chan struct{}, 1)
r.admin = make(chan func(), 32) r.admin = make(chan func(), 32)
r.cryptokey.init(r.core) r.cryptokey.init(r.core)
// go r.mainLoop() r.tun.init(r.core, send, recv)
} }
// Starts the mainLoop goroutine. // Starts the mainLoop goroutine.
@ -267,25 +267,6 @@ func (r *router) sendPacket(bs []byte) {
// Drop packets if the session MTU is 0 - this means that one or other // Drop packets if the session MTU is 0 - this means that one or other
// side probably has their TUN adapter disabled // side probably has their TUN adapter disabled
if sinfo.getMTU() == 0 { if sinfo.getMTU() == 0 {
// Get the size of the oversized payload, up to a max of 900 bytes
window := 900
if len(bs) < window {
window = len(bs)
}
// Create the Destination Unreachable response
ptb := &icmp.DstUnreach{
Data: bs[:window],
}
// Create the ICMPv6 response from it
icmpv6Buf, err := r.core.tun.icmpv6.create_icmpv6_tun(
bs[8:24], bs[24:40],
ipv6.ICMPTypeDestinationUnreachable, 1, ptb)
if err == nil {
r.recv <- icmpv6Buf
}
// Don't continue - drop the packet // Don't continue - drop the packet
return return
} }
@ -304,7 +285,7 @@ func (r *router) sendPacket(bs []byte) {
} }
// Create the ICMPv6 response from it // Create the ICMPv6 response from it
icmpv6Buf, err := r.core.tun.icmpv6.create_icmpv6_tun( icmpv6Buf, err := r.tun.icmpv6.create_icmpv6_tun(
bs[8:24], bs[24:40], bs[8:24], bs[24:40],
ipv6.ICMPTypePacketTooBig, 0, ptb) ipv6.ICMPTypePacketTooBig, 0, ptb)
if err == nil { if err == nil {
@ -428,6 +409,10 @@ func (r *router) handleProto(packet []byte) {
r.handlePing(bs, &p.FromKey) r.handlePing(bs, &p.FromKey)
case wire_SessionPong: case wire_SessionPong:
r.handlePong(bs, &p.FromKey) r.handlePong(bs, &p.FromKey)
case wire_NodeInfoRequest:
fallthrough
case wire_NodeInfoResponse:
r.handleNodeInfo(bs, &p.FromKey)
case wire_DHTLookupRequest: case wire_DHTLookupRequest:
r.handleDHTReq(bs, &p.FromKey) r.handleDHTReq(bs, &p.FromKey)
case wire_DHTLookupResponse: case wire_DHTLookupResponse:
@ -472,6 +457,16 @@ func (r *router) handleDHTRes(bs []byte, fromKey *boxPubKey) {
r.core.dht.handleRes(&res) r.core.dht.handleRes(&res)
} }
// Decodes nodeinfo request
func (r *router) handleNodeInfo(bs []byte, fromKey *boxPubKey) {
req := nodeinfoReqRes{}
if !req.decode(bs) {
return
}
req.SendPermPub = *fromKey
r.core.nodeinfo.handleNodeInfo(&req)
}
// Passed a function to call. // Passed a function to call.
// This will send the function to r.admin and block until it finishes. // This will send the function to r.admin and block until it finishes.
// It's used by the admin socket to ask the router mainLoop goroutine about information in the session or dht structs, which cannot be read safely from outside that goroutine. // It's used by the admin socket to ask the router mainLoop goroutine about information in the session or dht structs, which cannot be read safely from outside that goroutine.

View File

@ -273,7 +273,7 @@ func (ss *sessions) createSession(theirPermKey *boxPubKey) *sessionInfo {
sinfo.mySesPriv = *priv sinfo.mySesPriv = *priv
sinfo.myNonce = *newBoxNonce() sinfo.myNonce = *newBoxNonce()
sinfo.theirMTU = 1280 sinfo.theirMTU = 1280
sinfo.myMTU = uint16(ss.core.tun.mtu) sinfo.myMTU = uint16(ss.core.router.tun.mtu)
now := time.Now() now := time.Now()
sinfo.time = now sinfo.time = now
sinfo.mtuTime = now sinfo.mtuTime = now

View File

@ -17,11 +17,9 @@ const tun_IPv6_HEADER_LENGTH = 40
const tun_ETHER_HEADER_LENGTH = 14 const tun_ETHER_HEADER_LENGTH = 14
// Represents a running TUN/TAP interface. // Represents a running TUN/TAP interface.
type tunDevice struct { type tunAdapter struct {
core *Core Adapter
icmpv6 icmpv6 icmpv6 icmpv6
send chan<- []byte
recv <-chan []byte
mtu int mtu int
iface *water.Interface iface *water.Interface
} }
@ -36,14 +34,14 @@ func getSupportedMTU(mtu int) int {
} }
// Initialises the TUN/TAP adapter. // Initialises the TUN/TAP adapter.
func (tun *tunDevice) init(core *Core) { func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte) {
tun.core = core tun.Adapter.init(core, send, recv)
tun.icmpv6.init(tun) tun.icmpv6.init(tun)
} }
// Starts the setup process for the TUN/TAP adapter, and if successful, starts // Starts the setup process for the TUN/TAP adapter, and if successful, starts
// the read/write goroutines to handle packets on that interface. // the read/write goroutines to handle packets on that interface.
func (tun *tunDevice) start(ifname string, iftapmode bool, addr string, mtu int) error { func (tun *tunAdapter) start(ifname string, iftapmode bool, addr string, mtu int) error {
if ifname == "none" { if ifname == "none" {
return nil return nil
} }
@ -75,7 +73,7 @@ func (tun *tunDevice) start(ifname string, iftapmode bool, addr string, mtu int)
// Writes a packet to the TUN/TAP adapter. If the adapter is running in TAP // Writes a packet to the TUN/TAP adapter. If the adapter is running in TAP
// mode then additional ethernet encapsulation is added for the benefit of the // mode then additional ethernet encapsulation is added for the benefit of the
// host operating system. // host operating system.
func (tun *tunDevice) write() error { func (tun *tunAdapter) write() error {
for { for {
data := <-tun.recv data := <-tun.recv
if tun.iface == nil { if tun.iface == nil {
@ -164,7 +162,7 @@ func (tun *tunDevice) write() error {
// is running in TAP mode then the ethernet headers will automatically be // is running in TAP mode then the ethernet headers will automatically be
// processed and stripped if necessary. If an ICMPv6 packet is found, then // processed and stripped if necessary. If an ICMPv6 packet is found, then
// the relevant helper functions in icmpv6.go are called. // the relevant helper functions in icmpv6.go are called.
func (tun *tunDevice) read() error { func (tun *tunAdapter) read() error {
mtu := tun.mtu mtu := tun.mtu
if tun.iface.IsTAP() { if tun.iface.IsTAP() {
mtu += tun_ETHER_HEADER_LENGTH mtu += tun_ETHER_HEADER_LENGTH
@ -201,7 +199,7 @@ func (tun *tunDevice) read() error {
// Closes the TUN/TAP adapter. This is only usually called when the Yggdrasil // Closes the TUN/TAP adapter. This is only usually called when the Yggdrasil
// process stops. Typically this operation will happen quickly, but on macOS // process stops. Typically this operation will happen quickly, but on macOS
// it can block until a read operation is completed. // it can block until a read operation is completed.
func (tun *tunDevice) close() error { func (tun *tunAdapter) close() error {
if tun.iface == nil { if tun.iface == nil {
return nil return nil
} }

View File

@ -77,7 +77,7 @@ type in6_ifreq_lifetime struct {
// a system socket and making syscalls to the kernel. This is not refined though // a system socket and making syscalls to the kernel. This is not refined though
// and often doesn't work (if at all), therefore if a call fails, it resorts // and often doesn't work (if at all), therefore if a call fails, it resorts
// to calling "ifconfig" instead. // to calling "ifconfig" instead.
func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error { func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
var config water.Config var config water.Config
if ifname[:4] == "auto" { if ifname[:4] == "auto" {
ifname = "/dev/tap0" ifname = "/dev/tap0"
@ -103,7 +103,7 @@ func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int)
return tun.setupAddress(addr) return tun.setupAddress(addr)
} }
func (tun *tunDevice) setupAddress(addr string) error { func (tun *tunAdapter) setupAddress(addr string) error {
var sfd int var sfd int
var err error var err error

View File

@ -14,7 +14,7 @@ import (
) )
// Configures the "utun" adapter with the correct IPv6 address and MTU. // Configures the "utun" adapter with the correct IPv6 address and MTU.
func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error { func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
if iftapmode { if iftapmode {
tun.core.log.Printf("TAP mode is not supported on this platform, defaulting to TUN") tun.core.log.Printf("TAP mode is not supported on this platform, defaulting to TUN")
} }
@ -62,7 +62,7 @@ type ifreq struct {
// Sets the IPv6 address of the utun adapter. On Darwin/macOS this is done using // Sets the IPv6 address of the utun adapter. On Darwin/macOS this is done using
// a system socket and making direct syscalls to the kernel. // a system socket and making direct syscalls to the kernel.
func (tun *tunDevice) setupAddress(addr string) error { func (tun *tunAdapter) setupAddress(addr string) error {
var fd int var fd int
var err error var err error

View File

@ -13,7 +13,7 @@ import (
) )
// Configures the TAP adapter with the correct IPv6 address and MTU. // Configures the TAP adapter with the correct IPv6 address and MTU.
func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error { func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
var config water.Config var config water.Config
if iftapmode { if iftapmode {
config = water.Config{DeviceType: water.TAP} config = water.Config{DeviceType: water.TAP}
@ -48,7 +48,7 @@ func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int)
// is used to do this, so there is not a hard requirement on "ip" or "ifconfig" // is used to do this, so there is not a hard requirement on "ip" or "ifconfig"
// to exist on the system, but this will fail if Netlink is not present in the // to exist on the system, but this will fail if Netlink is not present in the
// kernel (it nearly always is). // kernel (it nearly always is).
func (tun *tunDevice) setupAddress(addr string) error { func (tun *tunAdapter) setupAddress(addr string) error {
// Set address // Set address
var netIF *net.Interface var netIF *net.Interface
ifces, err := net.Interfaces() ifces, err := net.Interfaces()

View File

@ -9,7 +9,7 @@ import water "github.com/yggdrasil-network/water"
// Creates the TUN/TAP adapter, if supported by the Water library. Note that // Creates the TUN/TAP adapter, if supported by the Water library. Note that
// no guarantees are made at this point on an unsupported platform. // no guarantees are made at this point on an unsupported platform.
func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error { func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
var config water.Config var config water.Config
if iftapmode { if iftapmode {
config = water.Config{DeviceType: water.TAP} config = water.Config{DeviceType: water.TAP}
@ -27,7 +27,7 @@ func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int)
// We don't know how to set the IPv6 address on an unknown platform, therefore // We don't know how to set the IPv6 address on an unknown platform, therefore
// write about it to stdout and don't try to do anything further. // write about it to stdout and don't try to do anything further.
func (tun *tunDevice) setupAddress(addr string) error { func (tun *tunAdapter) setupAddress(addr string) error {
tun.core.log.Println("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr) tun.core.log.Println("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr)
return nil return nil
} }

View File

@ -13,7 +13,7 @@ import (
// Configures the TAP adapter with the correct IPv6 address and MTU. On Windows // Configures the TAP adapter with the correct IPv6 address and MTU. On Windows
// we don't make use of a direct operating system API to do this - we instead // we don't make use of a direct operating system API to do this - we instead
// delegate the hard work to "netsh". // delegate the hard work to "netsh".
func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error { func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
if !iftapmode { if !iftapmode {
tun.core.log.Printf("TUN mode is not supported on this platform, defaulting to TAP") tun.core.log.Printf("TUN mode is not supported on this platform, defaulting to TAP")
} }
@ -65,7 +65,7 @@ func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int)
} }
// Sets the MTU of the TAP adapter. // Sets the MTU of the TAP adapter.
func (tun *tunDevice) setupMTU(mtu int) error { func (tun *tunAdapter) setupMTU(mtu int) error {
// Set MTU // Set MTU
cmd := exec.Command("netsh", "interface", "ipv6", "set", "subinterface", cmd := exec.Command("netsh", "interface", "ipv6", "set", "subinterface",
fmt.Sprintf("interface=%s", tun.iface.Name()), fmt.Sprintf("interface=%s", tun.iface.Name()),
@ -82,7 +82,7 @@ func (tun *tunDevice) setupMTU(mtu int) error {
} }
// Sets the IPv6 address of the TAP adapter. // Sets the IPv6 address of the TAP adapter.
func (tun *tunDevice) setupAddress(addr string) error { func (tun *tunAdapter) setupAddress(addr string) error {
// Set address // Set address
cmd := exec.Command("netsh", "interface", "ipv6", "add", "address", cmd := exec.Command("netsh", "interface", "ipv6", "add", "address",
fmt.Sprintf("interface=%s", tun.iface.Name()), fmt.Sprintf("interface=%s", tun.iface.Name()),

View File

@ -16,6 +16,8 @@ const (
wire_SessionPong // inside protocol traffic header wire_SessionPong // inside protocol traffic header
wire_DHTLookupRequest // inside protocol traffic header wire_DHTLookupRequest // inside protocol traffic header
wire_DHTLookupResponse // inside protocol traffic header wire_DHTLookupResponse // inside protocol traffic header
wire_NodeInfoRequest // inside protocol traffic header
wire_NodeInfoResponse // inside protocol traffic header
) )
// Calls wire_put_uint64 on a nil slice. // Calls wire_put_uint64 on a nil slice.
@ -353,6 +355,47 @@ func (p *sessionPing) decode(bs []byte) bool {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Encodes a nodeinfoReqRes into its wire format.
func (p *nodeinfoReqRes) encode() []byte {
var pTypeVal uint64
if p.IsResponse {
pTypeVal = wire_NodeInfoResponse
} else {
pTypeVal = wire_NodeInfoRequest
}
bs := wire_encode_uint64(pTypeVal)
bs = wire_put_coords(p.SendCoords, bs)
if pTypeVal == wire_NodeInfoResponse {
bs = append(bs, p.NodeInfo...)
}
return bs
}
// Decodes an encoded nodeinfoReqRes into the struct, returning true if successful.
func (p *nodeinfoReqRes) decode(bs []byte) bool {
var pType uint64
switch {
case !wire_chop_uint64(&pType, &bs):
return false
case pType != wire_NodeInfoRequest && pType != wire_NodeInfoResponse:
return false
case !wire_chop_coords(&p.SendCoords, &bs):
return false
}
if p.IsResponse = pType == wire_NodeInfoResponse; p.IsResponse {
if len(bs) == 0 {
return false
}
p.NodeInfo = make(nodeinfoPayload, len(bs))
if !wire_chop_slice(p.NodeInfo[:], &bs) {
return false
}
}
return true
}
////////////////////////////////////////////////////////////////////////////////
// Encodes a dhtReq into its wire format. // Encodes a dhtReq into its wire format.
func (r *dhtReq) encode() []byte { func (r *dhtReq) encode() []byte {
coords := wire_encode_coords(r.Coords) coords := wire_encode_coords(r.Coords)