5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-11-23 02:01:36 +00:00

Some attempt at exchanging session metadata over the wire (broken)

This commit is contained in:
Neil Alexander 2018-10-21 22:58:27 +01:00
parent 4f435705e3
commit a1b72c16d8
No known key found for this signature in database
GPG Key ID: A02A2019A2BB0944
9 changed files with 228 additions and 83 deletions

View File

@ -470,7 +470,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)},
{"friendly_name", a.core.friendlyName}, {"name", a.core.metadata.name},
{"location", a.core.metadata.location},
{"contact", a.core.metadata.contact},
} }
return &self return &self
} }
@ -494,7 +496,6 @@ func (a *admin) getData_getPeers() []admin_nodeInfo {
{"bytes_sent", atomic.LoadUint64(&p.bytesSent)}, {"bytes_sent", atomic.LoadUint64(&p.bytesSent)},
{"bytes_recvd", atomic.LoadUint64(&p.bytesRecvd)}, {"bytes_recvd", atomic.LoadUint64(&p.bytesRecvd)},
{"endpoint", p.endpoint}, {"endpoint", p.endpoint},
{"friendly_name", p.friendlyName},
} }
peerInfos = append(peerInfos, info) peerInfos = append(peerInfos, info)
} }
@ -520,7 +521,6 @@ func (a *admin) getData_getSwitchPeers() []admin_nodeInfo {
{"bytes_sent", atomic.LoadUint64(&peer.bytesSent)}, {"bytes_sent", atomic.LoadUint64(&peer.bytesSent)},
{"bytes_recvd", atomic.LoadUint64(&peer.bytesRecvd)}, {"bytes_recvd", atomic.LoadUint64(&peer.bytesRecvd)},
{"endpoint", peer.endpoint}, {"endpoint", peer.endpoint},
{"friendly_name", peer.friendlyName},
} }
peerInfos = append(peerInfos, info) peerInfos = append(peerInfos, info)
} }

View File

@ -2,7 +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 {
FriendlyName string `comment:"Friendly name for this node. It is visible to direct peers."` 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."` 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."`
Peers []string `comment:"List of connection strings for static peers in URI format, i.e.\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, i.e.\ntcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j."`
@ -35,3 +35,9 @@ type SessionFirewall struct {
WhitelistEncryptionPublicKeys []string `comment:"List of public keys from which network traffic is always accepted,\nregardless of AllowFromDirect or AllowFromRemote."` WhitelistEncryptionPublicKeys []string `comment:"List of public keys from which network traffic is always accepted,\nregardless of AllowFromDirect or AllowFromRemote."`
BlacklistEncryptionPublicKeys []string `comment:"List of public keys from which network traffic is always rejected,\nregardless of the whitelist, AllowFromDirect or AllowFromRemote."` BlacklistEncryptionPublicKeys []string `comment:"List of public keys from which network traffic is always rejected,\nregardless of the whitelist, AllowFromDirect or AllowFromRemote."`
} }
type Metadata struct {
Name string
Location string
Contact string
}

View File

@ -16,31 +16,31 @@ import (
// object for each Yggdrasil node you plan to run. // object for each Yggdrasil node you plan to run.
type Core struct { type Core struct {
// This is the main data structure that holds everything else for a node // This is the main data structure that holds everything else for a node
boxPub boxPubKey boxPub boxPubKey
boxPriv boxPrivKey boxPriv boxPrivKey
sigPub sigPubKey sigPub sigPubKey
sigPriv sigPrivKey sigPriv sigPrivKey
friendlyName string metadata metadata
switchTable switchTable switchTable switchTable
peers peers peers peers
sigs sigManager sigs sigManager
sessions sessions sessions sessions
router router router router
dht dht dht dht
tun tunDevice tun tunDevice
admin admin admin admin
searches searches searches searches
multicast multicast multicast multicast
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
} }
func (c *Core) init(bpub *boxPubKey, func (c *Core) init(bpub *boxPubKey,
bpriv *boxPrivKey, bpriv *boxPrivKey,
spub *sigPubKey, spub *sigPubKey,
spriv *sigPrivKey, spriv *sigPrivKey,
friendlyname string) { 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,7 +51,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.friendlyName = friendlyname c.metadata = metadata
c.admin.core = c c.admin.core = c
c.sigs.init() c.sigs.init()
c.searches.init(c) c.searches.init(c)
@ -65,11 +65,8 @@ func (c *Core) init(bpub *boxPubKey,
} }
// Gets the friendly name of this node, as specified in the NodeConfig. // Gets the friendly name of this node, as specified in the NodeConfig.
func (c *Core) GetFriendlyName() string { func (c *Core) GetMeta() metadata {
if c.friendlyName == "" { return c.metadata
return "(none)"
}
return c.friendlyName
} }
// Starts up Yggdrasil using the provided NodeConfig, and outputs debug logging // Starts up Yggdrasil using the provided NodeConfig, and outputs debug logging
@ -105,7 +102,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, nc.FriendlyName) 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

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

View File

@ -79,34 +79,34 @@ 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, friendlyname 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,
friendlyName: friendlyname, metadata: metadata,
firstSeen: now, firstSeen: now,
doSend: make(chan struct{}, 1), doSend: make(chan struct{}, 1),
core: ps.core} core: ps.core}
ps.mutex.Lock() ps.mutex.Lock()
defer ps.mutex.Unlock() defer ps.mutex.Unlock()
oldPorts := ps.getPorts() oldPorts := ps.getPorts()

View File

@ -47,7 +47,7 @@ func (r *router) init(core *Core) {
r.core = core r.core = core
r.addr = *address_addrForNodeID(&r.core.dht.nodeID) r.addr = *address_addrForNodeID(&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)", r.core.GetFriendlyName()) 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 {
@ -324,6 +324,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:
@ -368,6 +372,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 {
@ -431,6 +441,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(), "(none)") 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)