5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-11-10 07:30:27 +00:00

Merge branch 'descriptive' into metadata

This commit is contained in:
Neil Alexander 2018-12-12 18:04:49 +00:00
commit 6200136fce
No known key found for this signature in database
GPG Key ID: A02A2019A2BB0944
11 changed files with 219 additions and 50 deletions

View File

@ -256,6 +256,13 @@ func main() {
if buildversion, ok := v.(map[string]interface{})["build_version"].(string); ok && buildversion != "unknown" { if buildversion, ok := v.(map[string]interface{})["build_version"].(string); ok && buildversion != "unknown" {
fmt.Println("Build version:", buildversion) fmt.Println("Build version:", buildversion)
} }
if friendlyname, ok := v.(map[string]interface{})["friendly_name"].(string); ok {
if friendlyname == "" {
fmt.Println("Friendly name: (none)")
} else {
fmt.Println("Friendly name:", friendlyname)
}
}
fmt.Println("IPv6 address:", k) fmt.Println("IPv6 address:", k)
if subnet, ok := v.(map[string]interface{})["subnet"].(string); ok { if subnet, ok := v.(map[string]interface{})["subnet"].(string); ok {
fmt.Println("IPv6 subnet:", subnet) fmt.Println("IPv6 subnet:", subnet)

View File

@ -2,6 +2,7 @@ package config
// NodeConfig defines all configuration values needed to run a signle yggdrasil node // NodeConfig defines all configuration values needed to run a signle yggdrasil node
type NodeConfig struct { type NodeConfig struct {
Metadata Metadata `comment:"Optional node metadata. Entirely optional but visible to all\npeers and nodes with open sessions."`
Listen string `comment:"Listen address for peer connections. Default is to listen for all\nTCP connections over IPv4 and IPv6 with a random port."` Listen string `comment:"Listen address for peer connections. Default is to listen for all\nTCP connections over IPv4 and IPv6 with a random port."`
AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."` AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."`
Peers []string `comment:"List of connection strings for static peers in URI format, e.g.\ntcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j."` Peers []string `comment:"List of connection strings for static peers in URI format, e.g.\ntcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j."`
@ -51,3 +52,10 @@ type TunnelRouting struct {
type SwitchOptions struct { type SwitchOptions struct {
MaxTotalQueueSize uint64 `comment:"Maximum size of all switch queues combined (in bytes)."` MaxTotalQueueSize uint64 `comment:"Maximum size of all switch queues combined (in bytes)."`
} }
// Optional metadata - format subject to change
type Metadata struct {
Name string
Location string
Contact string
}

View File

@ -574,6 +574,9 @@ func (a *admin) getData_getSelf() *admin_nodeInfo {
{"ip", a.core.GetAddress().String()}, {"ip", a.core.GetAddress().String()},
{"subnet", a.core.GetSubnet().String()}, {"subnet", a.core.GetSubnet().String()},
{"coords", fmt.Sprint(coords)}, {"coords", fmt.Sprint(coords)},
{"name", a.core.metadata.name},
{"location", a.core.metadata.location},
{"contact", a.core.metadata.contact},
} }
if name := GetBuildName(); name != "unknown" { if name := GetBuildName(); name != "unknown" {
self = append(self, admin_pair{"build_name", name}) self = append(self, admin_pair{"build_name", name})

View File

@ -23,6 +23,7 @@ type Core struct {
boxPriv boxPrivKey boxPriv boxPrivKey
sigPub sigPubKey sigPub sigPubKey
sigPriv sigPrivKey sigPriv sigPrivKey
metadata metadata
switchTable switchTable switchTable switchTable
peers peers peers peers
sessions sessions sessions sessions
@ -40,7 +41,8 @@ type Core struct {
func (c *Core) init(bpub *boxPubKey, func (c *Core) init(bpub *boxPubKey,
bpriv *boxPrivKey, bpriv *boxPrivKey,
spub *sigPubKey, spub *sigPubKey,
spriv *sigPrivKey) { spriv *sigPrivKey,
metadata metadata) {
// TODO separate init and start functions // TODO separate init and start functions
// Init sets up structs // Init sets up structs
// Start launches goroutines that depend on structs being set up // Start launches goroutines that depend on structs being set up
@ -51,6 +53,7 @@ func (c *Core) init(bpub *boxPubKey,
} }
c.boxPub, c.boxPriv = *bpub, *bpriv c.boxPub, c.boxPriv = *bpub, *bpriv
c.sigPub, c.sigPriv = *spub, *spriv c.sigPub, c.sigPriv = *spub, *spriv
c.metadata = metadata
c.admin.core = c c.admin.core = c
c.searches.init(c) c.searches.init(c)
c.dht.init(c) c.dht.init(c)
@ -80,6 +83,11 @@ func GetBuildVersion() string {
return buildVersion return buildVersion
} }
// Gets the friendly name of this node, as specified in the NodeConfig.
func (c *Core) GetMeta() metadata {
return c.metadata
}
// Starts up Yggdrasil using the provided NodeConfig, and outputs debug logging // Starts up Yggdrasil using the provided NodeConfig, and outputs debug logging
// through the provided log.Logger. The started stack will include TCP and UDP // through the provided log.Logger. The started stack will include TCP and UDP
// sockets, a multicast discovery socket, an admin socket, router, switch and // sockets, a multicast discovery socket, an admin socket, router, switch and
@ -121,7 +129,13 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
copy(sigPub[:], sigPubHex) copy(sigPub[:], sigPubHex)
copy(sigPriv[:], sigPrivHex) copy(sigPriv[:], sigPrivHex)
c.init(&boxPub, &boxPriv, &sigPub, &sigPriv) meta := metadata{
name: nc.Metadata.Name,
location: nc.Metadata.Location,
contact: nc.Metadata.Contact,
}
c.init(&boxPub, &boxPriv, &sigPub, &sigPriv, meta)
c.admin.init(c, nc.AdminListen) c.admin.init(c, nc.AdminListen)
if err := c.tcp.init(c, nc.Listen, nc.ReadTimeout); err != nil { if err := c.tcp.init(c, nc.Listen, nc.ReadTimeout); err != nil {

View File

@ -50,7 +50,7 @@ func StartProfiler(log *log.Logger) error {
func (c *Core) Init() { func (c *Core) Init() {
bpub, bpriv := newBoxKeys() bpub, bpriv := newBoxKeys()
spub, spriv := newSigKeys() spub, spriv := newSigKeys()
c.init(bpub, bpriv, spub, spriv) c.init(bpub, bpriv, spub, spriv, metadata{})
c.switchTable.start() c.switchTable.start()
c.router.start() c.router.start()
} }
@ -84,7 +84,7 @@ func (c *Core) DEBUG_getPeers() *peers {
func (ps *peers) DEBUG_newPeer(box boxPubKey, sig sigPubKey, link boxSharedKey) *peer { func (ps *peers) DEBUG_newPeer(box boxPubKey, sig sigPubKey, link boxSharedKey) *peer {
//in <-chan []byte, //in <-chan []byte,
//out chan<- []byte) *peer { //out chan<- []byte) *peer {
return ps.newPeer(&box, &sig, &link, "(simulator)") //, in, out) return ps.newPeer(&box, &sig, &link, "(simulator)", metadata{}) //, in, out)
} }
/* /*
@ -356,7 +356,7 @@ func (c *Core) DEBUG_init(bpub []byte,
copy(boxPriv[:], bpriv) copy(boxPriv[:], bpriv)
copy(sigPub[:], spub) copy(sigPub[:], spub)
copy(sigPriv[:], spriv) copy(sigPriv[:], spriv)
c.init(&boxPub, &boxPriv, &sigPub, &sigPriv) c.init(&boxPub, &boxPriv, &sigPub, &sigPriv, metadata{})
if err := c.router.start(); err != nil { if err := c.router.start(); err != nil {
panic(err) panic(err)

View File

@ -0,0 +1,7 @@
package yggdrasil
type metadata struct {
name string
location string
contact string
}

View File

@ -79,30 +79,31 @@ type peer struct {
bytesSent uint64 // To track bandwidth usage for getPeers bytesSent uint64 // To track bandwidth usage for getPeers
bytesRecvd uint64 // To track bandwidth usage for getPeers bytesRecvd uint64 // To track bandwidth usage for getPeers
// BUG: sync/atomic, 32 bit platforms need the above to be the first element // BUG: sync/atomic, 32 bit platforms need the above to be the first element
core *Core core *Core
port switchPort port switchPort
box boxPubKey box boxPubKey
sig sigPubKey sig sigPubKey
shared boxSharedKey shared boxSharedKey
linkShared boxSharedKey linkShared boxSharedKey
endpoint string endpoint string
friendlyName string metadata metadata
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
dinfo *dhtInfo // used to keep the DHT working dinfo *dhtInfo // used to keep the DHT working
out func([]byte) // Set up by whatever created the peers struct, used to send packets to other nodes out func([]byte) // Set up by whatever created the peers struct, used to send packets to other nodes
close func() // Called when a peer is removed, to close the underlying connection, or via admin api close func() // Called when a peer is removed, to close the underlying connection, or via admin api
} }
// Creates a new peer with the specified box, sig, and linkShared keys, using the lowest unocupied port number. // Creates a new peer with the specified box, sig, and linkShared keys, using the lowest unocupied port number.
func (ps *peers) newPeer(box *boxPubKey, sig *sigPubKey, linkShared *boxSharedKey, endpoint string) *peer { func (ps *peers) newPeer(box *boxPubKey, sig *sigPubKey, linkShared *boxSharedKey, endpoint string, metadata metadata) *peer {
now := time.Now() now := time.Now()
p := peer{box: *box, p := peer{box: *box,
sig: *sig, sig: *sig,
shared: *getSharedKey(&ps.core.boxPriv, box), shared: *getSharedKey(&ps.core.boxPriv, box),
linkShared: *linkShared, linkShared: *linkShared,
endpoint: endpoint, endpoint: endpoint,
metadata: metadata,
firstSeen: now, firstSeen: now,
doSend: make(chan struct{}, 1), doSend: make(chan struct{}, 1),
core: ps.core} core: ps.core}

View File

@ -58,7 +58,7 @@ func (r *router) init(core *Core) {
r.addr = *address_addrForNodeID(&r.core.dht.nodeID) r.addr = *address_addrForNodeID(&r.core.dht.nodeID)
r.subnet = *address_subnetForNodeID(&r.core.dht.nodeID) r.subnet = *address_subnetForNodeID(&r.core.dht.nodeID)
in := make(chan []byte, 32) // TODO something better than this... in := make(chan []byte, 32) // TODO something better than this...
p := r.core.peers.newPeer(&r.core.boxPub, &r.core.sigPub, &boxSharedKey{}, "(self)") p := r.core.peers.newPeer(&r.core.boxPub, &r.core.sigPub, &boxSharedKey{}, "(self)", r.core.metadata)
p.out = func(packet []byte) { p.out = func(packet []byte) {
// This is to make very sure it never blocks // This is to make very sure it never blocks
select { select {
@ -428,6 +428,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_SessionMetaRequest:
fallthrough
case wire_SessionMetaResponse:
r.handleMeta(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 +476,17 @@ func (r *router) handleDHTRes(bs []byte, fromKey *boxPubKey) {
r.core.dht.handleRes(&res) r.core.dht.handleRes(&res)
} }
// Decodes meta request
func (r *router) handleMeta(bs []byte, fromKey *boxPubKey) {
req := sessionMeta{}
if !req.decode(bs) {
return
}
req.SendPermPub = *fromKey
r.core.log.Printf("handleMeta: %+v\n", req)
r.core.sessions.handleMeta(&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

@ -13,34 +13,37 @@ import (
// All the information we know about an active session. // All the information we know about an active session.
// This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API. // This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API.
type sessionInfo struct { type sessionInfo struct {
core *Core core *Core
theirAddr address theirAddr address
theirSubnet subnet theirSubnet subnet
theirPermPub boxPubKey theirPermPub boxPubKey
theirSesPub boxPubKey theirSesPub boxPubKey
mySesPub boxPubKey mySesPub boxPubKey
mySesPriv boxPrivKey mySesPriv boxPrivKey
sharedSesKey boxSharedKey // derived from session keys sharedSesKey boxSharedKey // derived from session keys
theirHandle handle theirHandle handle
myHandle handle myHandle handle
theirNonce boxNonce theirNonce boxNonce
myNonce boxNonce myNonce boxNonce
theirMTU uint16 metaReqTime time.Time
myMTU uint16 metaResTime time.Time
wasMTUFixed bool // Was the MTU fixed by a receive error? theirMetadata metadata
time time.Time // Time we last received a packet theirMTU uint16
coords []byte // coords of destination myMTU uint16
packet []byte // a buffered packet, sent immediately on ping/pong wasMTUFixed bool // Was the MTU fixed by a receive error?
init bool // Reset if coords change time time.Time // Time we last received a packet
send chan []byte coords []byte // coords of destination
recv chan *wire_trafficPacket packet []byte // a buffered packet, sent immediately on ping/pong
nonceMask uint64 init bool // Reset if coords change
tstamp int64 // tstamp from their last session ping, replay attack mitigation send chan []byte
mtuTime time.Time // time myMTU was last changed recv chan *wire_trafficPacket
pingTime time.Time // time the first ping was sent since the last received packet nonceMask uint64
pingSend time.Time // time the last ping was sent tstamp int64 // tstamp from their last session ping, replay attack mitigation
bytesSent uint64 // Bytes of real traffic sent in this session mtuTime time.Time // time myMTU was last changed
bytesRecvd uint64 // Bytes of real traffic received in this session pingTime time.Time // time the first ping was sent since the last received packet
pingSend time.Time // time the last ping was sent
bytesSent uint64 // Bytes of real traffic sent in this session
bytesRecvd uint64 // Bytes of real traffic received in this session
} }
// Represents a session ping/pong packet, andincludes information like public keys, a session handle, coords, a timestamp to prevent replays, and the tun/tap MTU. // Represents a session ping/pong packet, andincludes information like public keys, a session handle, coords, a timestamp to prevent replays, and the tun/tap MTU.
@ -54,6 +57,13 @@ type sessionPing struct {
MTU uint16 MTU uint16
} }
// Represents a session metadata packet.
type sessionMeta struct {
SendPermPub boxPubKey // Sender's permanent key
IsResponse bool
Metadata metadata
}
// Updates session info in response to a ping, after checking that the ping is OK. // Updates session info in response to a ping, after checking that the ping is OK.
// Returns true if the session was updated, or false otherwise. // Returns true if the session was updated, or false otherwise.
func (s *sessionInfo) update(p *sessionPing) bool { func (s *sessionInfo) update(p *sessionPing) bool {
@ -466,6 +476,66 @@ func (ss *sessions) handlePing(ping *sessionPing) {
bs, sinfo.packet = sinfo.packet, nil bs, sinfo.packet = sinfo.packet, nil
ss.core.router.sendPacket(bs) ss.core.router.sendPacket(bs)
} }
if time.Since(sinfo.metaResTime).Minutes() > 15 {
if time.Since(sinfo.metaReqTime).Minutes() > 1 {
ss.sendMeta(sinfo, false)
}
}
}
func (ss *sessions) sendMeta(sinfo *sessionInfo, isResponse bool) {
meta := sessionMeta{
IsResponse: isResponse,
Metadata: metadata{
name: "some.name.com", //[]byte(ss.core.friendlyName)[0:len(ss.core.friendlyName):32],
location: "Some Place",
contact: "someone@somewhere.com",
},
}
bs := meta.encode()
shared := ss.getSharedKey(&ss.core.boxPriv, &sinfo.theirPermPub)
payload, nonce := boxSeal(shared, bs, nil)
p := wire_protoTrafficPacket{
Coords: sinfo.coords,
ToKey: sinfo.theirPermPub,
FromKey: ss.core.boxPub,
Nonce: *nonce,
Payload: payload,
}
packet := p.encode()
ss.core.router.out(packet)
if isResponse {
ss.core.log.Println("Sent meta response to", sinfo.theirAddr)
} else {
ss.core.log.Println("Sent meta request to", sinfo.theirAddr)
sinfo.metaReqTime = time.Now()
}
}
// Handles a meta request/response.
func (ss *sessions) handleMeta(meta *sessionMeta) {
// Get the corresponding session (or create a new session)
sinfo, isIn := ss.getByTheirPerm(&meta.SendPermPub)
// Check the session firewall
if !isIn && ss.sessionFirewallEnabled {
if !ss.isSessionAllowed(&meta.SendPermPub, false) {
return
}
}
if !isIn || sinfo.timedout() {
return
}
if meta.IsResponse {
ss.core.log.Println("Received meta response", string(meta.Metadata.name), "from", sinfo.theirAddr)
sinfo.theirMetadata = meta.Metadata
sinfo.metaResTime = time.Now()
ss.core.log.Println("- name:", meta.Metadata.name)
ss.core.log.Println("- contact:", meta.Metadata.contact)
ss.core.log.Println("- location:", meta.Metadata.location)
} else {
ss.core.log.Println("Received meta request", string(meta.Metadata.name), "from", sinfo.theirAddr)
ss.sendMeta(sinfo, true)
}
} }
// Used to subtract one nonce from another, staying in the range +- 64. // Used to subtract one nonce from another, staying in the range +- 64.

View File

@ -287,7 +287,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
}() }()
// Note that multiple connections to the same node are allowed // Note that multiple connections to the same node are allowed
// E.g. over different interfaces // E.g. over different interfaces
p := iface.core.peers.newPeer(&info.box, &info.sig, getSharedKey(myLinkPriv, &meta.link), sock.RemoteAddr().String()) p := iface.core.peers.newPeer(&info.box, &info.sig, getSharedKey(myLinkPriv, &meta.link), sock.RemoteAddr().String(), metadata{})
p.linkOut = make(chan []byte, 1) p.linkOut = make(chan []byte, 1)
in := func(bs []byte) { in := func(bs []byte) {
p.handlePacket(bs) p.handlePacket(bs)

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_SessionMetaRequest // inside protocol traffic header
wire_SessionMetaResponse // inside protocol traffic header
) )
// Calls wire_put_uint64 on a nil slice. // Calls wire_put_uint64 on a nil slice.
@ -353,6 +355,48 @@ func (p *sessionPing) decode(bs []byte) bool {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Encodes a sessionPing into its wire format.
func (p *sessionMeta) encode() []byte {
var pTypeVal uint64
if p.IsResponse {
pTypeVal = wire_SessionMetaResponse
} else {
pTypeVal = wire_SessionMetaRequest
}
bs := wire_encode_uint64(pTypeVal)
if p.IsResponse {
bs = append(bs, p.Metadata.name...)
bs = append(bs, p.Metadata.location...)
bs = append(bs, p.Metadata.contact...)
}
return bs
}
// Decodes an encoded sessionPing into the struct, returning true if successful.
func (p *sessionMeta) decode(bs []byte) bool {
var pType uint64
switch {
case !wire_chop_uint64(&pType, &bs):
return false
case pType != wire_SessionMetaRequest && pType != wire_SessionMetaResponse:
return false
}
p.IsResponse = pType == wire_SessionMetaResponse
if p.IsResponse {
switch {
case !wire_chop_slice([]byte(p.Metadata.name), &bs):
return false
case !wire_chop_slice([]byte(p.Metadata.location), &bs):
return false
case !wire_chop_slice([]byte(p.Metadata.contact), &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)