5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-11-10 06:20:26 +00:00

make the main library reconfiguration more actor-friendly

This commit is contained in:
Arceliar 2019-08-25 12:10:59 -05:00
parent aa30c6cc98
commit a3d4d8125b
9 changed files with 144 additions and 159 deletions

View File

@ -117,20 +117,21 @@ func (c *Core) UpdateConfig(config *config.NodeConfig) {
errors := 0 errors := 0
components := []chan chan error{ // Each reconfigure function should pass any errors to the channel, then close it
c.router.searches.reconfigure, components := []func(chan error){
c.router.dht.reconfigure, c.link.reconfigure,
c.router.sessions.reconfigure,
c.peers.reconfigure, c.peers.reconfigure,
c.router.reconfigure, c.router.reconfigure,
c.router.dht.reconfigure,
c.router.searches.reconfigure,
c.router.sessions.reconfigure,
c.switchTable.reconfigure, c.switchTable.reconfigure,
c.link.reconfigure,
} }
for _, component := range components { for _, component := range components {
response := make(chan error) response := make(chan error)
component <- response go component(response)
if err := <-response; err != nil { for err := range response {
c.log.Errorln(err) c.log.Errorln(err)
errors++ errors++
} }

View File

@ -65,11 +65,10 @@ type dhtReqKey struct {
// The main DHT struct. // The main DHT struct.
type dht struct { type dht struct {
router *router router *router
reconfigure chan chan error nodeID crypto.NodeID
nodeID crypto.NodeID reqs map[dhtReqKey]time.Time // Keeps track of recent outstanding requests
reqs map[dhtReqKey]time.Time // Keeps track of recent outstanding requests callbacks map[dhtReqKey][]dht_callbackInfo // Search and admin lookup callbacks
callbacks map[dhtReqKey][]dht_callbackInfo // Search and admin lookup callbacks
// These next two could be replaced by a single linked list or similar... // These next two could be replaced by a single linked list or similar...
table map[crypto.NodeID]*dhtInfo table map[crypto.NodeID]*dhtInfo
imp []*dhtInfo imp []*dhtInfo
@ -78,18 +77,16 @@ type dht struct {
// Initializes the DHT. // Initializes the DHT.
func (t *dht) init(r *router) { func (t *dht) init(r *router) {
t.router = r t.router = r
t.reconfigure = make(chan chan error, 1)
go func() {
for {
e := <-t.reconfigure
e <- nil
}
}()
t.nodeID = *t.router.core.NodeID() t.nodeID = *t.router.core.NodeID()
t.callbacks = make(map[dhtReqKey][]dht_callbackInfo) t.callbacks = make(map[dhtReqKey][]dht_callbackInfo)
t.reset() t.reset()
} }
func (t *dht) reconfigure(e chan error) {
defer close(e)
// This is where reconfiguration would go, if we had anything to do
}
// Resets the DHT in response to coord changes. // Resets the DHT in response to coord changes.
// This empties all info from the DHT and drops outstanding requests. // This empties all info from the DHT and drops outstanding requests.
func (t *dht) reset() { func (t *dht) reset() {

View File

@ -21,11 +21,10 @@ import (
) )
type link struct { type link struct {
core *Core core *Core
reconfigure chan chan error mutex sync.RWMutex // protects interfaces below
mutex sync.RWMutex // protects interfaces below interfaces map[linkInfo]*linkInterface
interfaces map[linkInfo]*linkInterface tcp tcp // TCP interface support
tcp tcp // TCP interface support
// TODO timeout (to remove from switch), read from config.ReadTimeout // TODO timeout (to remove from switch), read from config.ReadTimeout
} }
@ -61,7 +60,6 @@ func (l *link) init(c *Core) error {
l.core = c l.core = c
l.mutex.Lock() l.mutex.Lock()
l.interfaces = make(map[linkInfo]*linkInterface) l.interfaces = make(map[linkInfo]*linkInterface)
l.reconfigure = make(chan chan error)
l.mutex.Unlock() l.mutex.Unlock()
if err := l.tcp.init(l); err != nil { if err := l.tcp.init(l); err != nil {
@ -69,22 +67,18 @@ func (l *link) init(c *Core) error {
return err return err
} }
go func() {
for {
e := <-l.reconfigure
tcpresponse := make(chan error)
l.tcp.reconfigure <- tcpresponse
if err := <-tcpresponse; err != nil {
e <- err
continue
}
e <- nil
}
}()
return nil return nil
} }
func (l *link) reconfigure(e chan error) {
defer close(e)
tcpResponse := make(chan error)
go l.tcp.reconfigure(tcpResponse)
for err := range tcpResponse {
e <- err
}
}
func (l *link) call(uri string, sintf string) error { func (l *link) call(uri string, sintf string) error {
u, err := url.Parse(uri) u, err := url.Parse(uri)
if err != nil { if err != nil {

View File

@ -21,10 +21,9 @@ import (
// In most cases, this involves passing the packet to the handler for outgoing traffic to another peer. // In most cases, this involves passing the packet to the handler for outgoing traffic to another peer.
// In other cases, it's link protocol traffic used to build the spanning tree, in which case this checks signatures and passes the message along to the switch. // In other cases, it's link protocol traffic used to build the spanning tree, in which case this checks signatures and passes the message along to the switch.
type peers struct { type peers struct {
core *Core core *Core
reconfigure chan chan error mutex sync.Mutex // Synchronize writes to atomic
mutex sync.Mutex // Synchronize writes to atomic ports atomic.Value //map[switchPort]*peer, use CoW semantics
ports atomic.Value //map[switchPort]*peer, use CoW semantics
} }
// Initializes the peers struct. // Initializes the peers struct.
@ -33,13 +32,11 @@ func (ps *peers) init(c *Core) {
defer ps.mutex.Unlock() defer ps.mutex.Unlock()
ps.putPorts(make(map[switchPort]*peer)) ps.putPorts(make(map[switchPort]*peer))
ps.core = c ps.core = c
ps.reconfigure = make(chan chan error, 1) }
go func() {
for { func (ps *peers) reconfigure(e chan error) {
e := <-ps.reconfigure defer close(e)
e <- nil // This is where reconfiguration would go, if we had anything to do
}
}()
} }
// Returns true if an incoming peer connection to a key is allowed, either // Returns true if an incoming peer connection to a key is allowed, either

View File

@ -35,24 +35,22 @@ import (
) )
// The router struct has channels to/from the adapter device and a self peer (0), which is how messages are passed between this node and the peers/switch layer. // The router struct has channels to/from the adapter device and a self peer (0), which is how messages are passed between this node and the peers/switch layer.
// The router's mainLoop goroutine is responsible for managing all information related to the dht, searches, and crypto sessions. // The router's phony.Inbox goroutine is responsible for managing all information related to the dht, searches, and crypto sessions.
type router struct { type router struct {
phony.Inbox phony.Inbox
core *Core core *Core
reconfigure chan chan error addr address.Address
addr address.Address subnet address.Subnet
subnet address.Subnet 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" dht dht
dht dht nodeinfo nodeinfo
nodeinfo nodeinfo searches searches
searches searches sessions sessions
sessions sessions
} }
// Initializes the router struct, which includes setting up channels to/from the adapter. // Initializes the router struct, which includes setting up channels to/from the adapter.
func (r *router) init(core *Core) { func (r *router) init(core *Core) {
r.core = core r.core = core
r.reconfigure = make(chan chan error, 1)
r.addr = *address.AddrForNodeID(&r.dht.nodeID) r.addr = *address.AddrForNodeID(&r.dht.nodeID)
r.subnet = *address.SubnetForNodeID(&r.dht.nodeID) r.subnet = *address.SubnetForNodeID(&r.dht.nodeID)
self := linkInterface{ self := linkInterface{
@ -75,10 +73,26 @@ func (r *router) init(core *Core) {
r.sessions.init(r) r.sessions.init(r)
} }
// Starts the mainLoop goroutine. func (r *router) reconfigure(e chan error) {
defer close(e)
var errs []error
// Reconfigure the router
<-r.SyncExec(func() {
current := r.core.config.GetCurrent()
err := r.nodeinfo.setNodeInfo(current.NodeInfo, current.NodeInfoPrivacy)
if err != nil {
errs = append(errs, err)
}
})
for _, err := range errs {
e <- err
}
}
// Starts the tickerLoop goroutine.
func (r *router) start() error { func (r *router) start() error {
r.core.log.Infoln("Starting router") r.core.log.Infoln("Starting router")
go r._mainLoop() go r.tickerLoop()
return nil return nil
} }
@ -108,24 +122,17 @@ func (r *router) reset(from phony.Actor) {
// TODO remove reconfigure so this is just a ticker loop // TODO remove reconfigure so this is just a ticker loop
// and then find something better than a ticker loop to schedule things... // and then find something better than a ticker loop to schedule things...
func (r *router) _mainLoop() { func (r *router) tickerLoop() {
ticker := time.NewTicker(time.Second) ticker := time.NewTicker(time.Second)
defer ticker.Stop() defer ticker.Stop()
for { for {
select { <-ticker.C
case <-ticker.C: <-r.SyncExec(func() {
<-r.SyncExec(func() { // Any periodic maintenance stuff goes here
// Any periodic maintenance stuff goes here r.core.switchTable.doMaintenance()
r.core.switchTable.doMaintenance() r.dht.doMaintenance()
r.dht.doMaintenance() r.sessions.cleanup()
r.sessions.cleanup() })
})
case e := <-r.reconfigure:
<-r.SyncExec(func() {
current := r.core.config.GetCurrent()
e <- r.nodeinfo.setNodeInfo(current.NodeInfo, current.NodeInfoPrivacy)
})
}
} }
} }

View File

@ -45,24 +45,21 @@ type searchInfo struct {
// This stores a map of active searches. // This stores a map of active searches.
type searches struct { type searches struct {
router *router router *router
reconfigure chan chan error searches map[crypto.NodeID]*searchInfo
searches map[crypto.NodeID]*searchInfo
} }
// Initializes the searches struct. // Initializes the searches struct.
func (s *searches) init(r *router) { func (s *searches) init(r *router) {
s.router = r s.router = r
s.reconfigure = make(chan chan error, 1)
go func() {
for {
e := <-s.reconfigure
e <- nil
}
}()
s.searches = make(map[crypto.NodeID]*searchInfo) s.searches = make(map[crypto.NodeID]*searchInfo)
} }
func (s *searches) reconfigure(e chan error) {
defer close(e)
// This is where reconfiguration would go, if we had anything to do
}
// Creates a new search info, adds it to the searches struct, and returns a pointer to the info. // Creates a new search info, adds it to the searches struct, and returns a pointer to the info.
func (s *searches) createSearch(dest *crypto.NodeID, mask *crypto.NodeID, callback func(*sessionInfo, error)) *searchInfo { func (s *searches) createSearch(dest *crypto.NodeID, mask *crypto.NodeID, callback func(*sessionInfo, error)) *searchInfo {
info := searchInfo{ info := searchInfo{

View File

@ -40,7 +40,6 @@ func (h nonceHeap) peek() *crypto.BoxNonce { return &h[0] }
type sessionInfo struct { type sessionInfo struct {
phony.Inbox // Protects all of the below, use it any time you read/change the contents of a session phony.Inbox // Protects all of the below, use it any time you read/change the contents of a session
sessions *sessions // sessions *sessions //
reconfigure chan chan error //
theirAddr address.Address // theirAddr address.Address //
theirSubnet address.Subnet // theirSubnet address.Subnet //
theirPermPub crypto.BoxPubKey // theirPermPub crypto.BoxPubKey //
@ -74,6 +73,11 @@ type sessionInfo struct {
callbacks []chan func() // Finished work from crypto workers callbacks []chan func() // Finished work from crypto workers
} }
func (sinfo *sessionInfo) reconfigure(e chan error) {
defer close(e)
// This is where reconfiguration would go, if we had anything to do
}
// TODO remove this, call SyncExec directly // TODO remove this, call SyncExec directly
func (sinfo *sessionInfo) doFunc(f func()) { func (sinfo *sessionInfo) doFunc(f func()) {
<-sinfo.SyncExec(f) <-sinfo.SyncExec(f)
@ -140,7 +144,6 @@ type sessions struct {
router *router router *router
listener *Listener listener *Listener
listenerMutex sync.Mutex listenerMutex sync.Mutex
reconfigure chan chan error
lastCleanup time.Time lastCleanup time.Time
isAllowedHandler func(pubkey *crypto.BoxPubKey, initiator bool) bool // Returns true or false if session setup is allowed isAllowedHandler func(pubkey *crypto.BoxPubKey, initiator bool) bool // Returns true or false if session setup is allowed
isAllowedMutex sync.RWMutex // Protects the above isAllowedMutex sync.RWMutex // Protects the above
@ -152,30 +155,28 @@ type sessions struct {
// Initializes the session struct. // Initializes the session struct.
func (ss *sessions) init(r *router) { func (ss *sessions) init(r *router) {
ss.router = r ss.router = r
ss.reconfigure = make(chan chan error, 1)
go func() {
for {
e := <-ss.reconfigure
responses := make(map[crypto.Handle]chan error)
for index, session := range ss.sinfos {
responses[index] = make(chan error)
session.reconfigure <- responses[index]
}
for _, response := range responses {
if err := <-response; err != nil {
e <- err
continue
}
}
e <- nil
}
}()
ss.permShared = make(map[crypto.BoxPubKey]*crypto.BoxSharedKey) ss.permShared = make(map[crypto.BoxPubKey]*crypto.BoxSharedKey)
ss.sinfos = make(map[crypto.Handle]*sessionInfo) ss.sinfos = make(map[crypto.Handle]*sessionInfo)
ss.byTheirPerm = make(map[crypto.BoxPubKey]*crypto.Handle) ss.byTheirPerm = make(map[crypto.BoxPubKey]*crypto.Handle)
ss.lastCleanup = time.Now() ss.lastCleanup = time.Now()
} }
func (ss *sessions) reconfigure(e chan error) {
defer close(e)
responses := make(map[crypto.Handle]chan error)
<-ss.router.SyncExec(func() {
for index, session := range ss.sinfos {
responses[index] = make(chan error)
go session.reconfigure(responses[index])
}
})
for _, response := range responses {
for err := range response {
e <- err
}
}
}
// Determines whether the session with a given publickey is allowed based on // Determines whether the session with a given publickey is allowed based on
// session firewall rules. // session firewall rules.
func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) bool { func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) bool {
@ -215,7 +216,6 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo {
} }
sinfo := sessionInfo{} sinfo := sessionInfo{}
sinfo.sessions = ss sinfo.sessions = ss
sinfo.reconfigure = make(chan chan error, 1)
sinfo.theirPermPub = *theirPermKey sinfo.theirPermPub = *theirPermKey
sinfo.sharedPermKey = *ss.getSharedKey(&ss.router.core.boxPriv, &sinfo.theirPermPub) sinfo.sharedPermKey = *ss.getSharedKey(&ss.router.core.boxPriv, &sinfo.theirPermPub)
pub, priv := crypto.NewBoxKeys() pub, priv := crypto.NewBoxKeys()

View File

@ -165,7 +165,6 @@ type switchData struct {
// All the information stored by the switch. // All the information stored by the switch.
type switchTable struct { type switchTable struct {
core *Core core *Core
reconfigure chan chan error
key crypto.SigPubKey // Our own key key crypto.SigPubKey // Our own key
time time.Time // Time when locator.tstamp was last updated time time.Time // Time when locator.tstamp was last updated
drop map[crypto.SigPubKey]int64 // Tstamp associated with a dropped root drop map[crypto.SigPubKey]int64 // Tstamp associated with a dropped root
@ -186,7 +185,6 @@ const SwitchQueueTotalMinSize = 4 * 1024 * 1024
func (t *switchTable) init(core *Core) { func (t *switchTable) init(core *Core) {
now := time.Now() now := time.Now()
t.core = core t.core = core
t.reconfigure = make(chan chan error, 1)
t.key = t.core.sigPub t.key = t.core.sigPub
locator := switchLocator{root: t.key, tstamp: now.Unix()} locator := switchLocator{root: t.key, tstamp: now.Unix()}
peers := make(map[switchPort]peerInfo) peers := make(map[switchPort]peerInfo)
@ -201,6 +199,13 @@ func (t *switchTable) init(core *Core) {
}) })
} }
func (t *switchTable) reconfigure(e chan error) {
go func() {
defer close(e)
// This is where reconfiguration would go, if we had anything useful to do.
}()
}
// Safely gets a copy of this node's locator. // Safely gets a copy of this node's locator.
func (t *switchTable) getLocator() switchLocator { func (t *switchTable) getLocator() switchLocator {
t.mutex.RLock() t.mutex.RLock()
@ -566,12 +571,7 @@ func (t *switchTable) getTable() lookupTable {
// Starts the switch worker // Starts the switch worker
func (t *switchTable) start() error { func (t *switchTable) start() error {
t.core.log.Infoln("Starting switch") t.core.log.Infoln("Starting switch")
go func() { // There's actually nothing to do to start it...
// TODO find a better way to handle reconfiguration... and have the switch do something with the new configuration
for ch := range t.reconfigure {
ch <- nil
}
}()
return nil return nil
} }

View File

@ -33,12 +33,11 @@ const tcp_ping_interval = (default_timeout * 2 / 3)
// The TCP listener and information about active TCP connections, to avoid duplication. // The TCP listener and information about active TCP connections, to avoid duplication.
type tcp struct { type tcp struct {
link *link link *link
reconfigure chan chan error mutex sync.Mutex // Protecting the below
mutex sync.Mutex // Protecting the below listeners map[string]*TcpListener
listeners map[string]*TcpListener calls map[string]struct{}
calls map[string]struct{} conns map[linkInfo](chan struct{})
conns map[linkInfo](chan struct{})
} }
// TcpListener is a stoppable TCP listener interface. These are typically // TcpListener is a stoppable TCP listener interface. These are typically
@ -76,49 +75,12 @@ func (t *tcp) getAddr() *net.TCPAddr {
// Initializes the struct. // Initializes the struct.
func (t *tcp) init(l *link) error { func (t *tcp) init(l *link) error {
t.link = l t.link = l
t.reconfigure = make(chan chan error, 1)
t.mutex.Lock() t.mutex.Lock()
t.calls = make(map[string]struct{}) t.calls = make(map[string]struct{})
t.conns = make(map[linkInfo](chan struct{})) t.conns = make(map[linkInfo](chan struct{}))
t.listeners = make(map[string]*TcpListener) t.listeners = make(map[string]*TcpListener)
t.mutex.Unlock() t.mutex.Unlock()
go func() {
for {
e := <-t.reconfigure
t.link.core.config.Mutex.RLock()
added := util.Difference(t.link.core.config.Current.Listen, t.link.core.config.Previous.Listen)
deleted := util.Difference(t.link.core.config.Previous.Listen, t.link.core.config.Current.Listen)
t.link.core.config.Mutex.RUnlock()
if len(added) > 0 || len(deleted) > 0 {
for _, a := range added {
if a[:6] != "tcp://" {
continue
}
if _, err := t.listen(a[6:]); err != nil {
e <- err
continue
}
}
for _, d := range deleted {
if d[:6] != "tcp://" {
continue
}
t.mutex.Lock()
if listener, ok := t.listeners[d[6:]]; ok {
t.mutex.Unlock()
listener.Stop <- true
} else {
t.mutex.Unlock()
}
}
e <- nil
} else {
e <- nil
}
}
}()
t.link.core.config.Mutex.RLock() t.link.core.config.Mutex.RLock()
defer t.link.core.config.Mutex.RUnlock() defer t.link.core.config.Mutex.RUnlock()
for _, listenaddr := range t.link.core.config.Current.Listen { for _, listenaddr := range t.link.core.config.Current.Listen {
@ -133,6 +95,36 @@ func (t *tcp) init(l *link) error {
return nil return nil
} }
func (t *tcp) reconfigure(e chan error) {
defer close(e)
t.link.core.config.Mutex.RLock()
added := util.Difference(t.link.core.config.Current.Listen, t.link.core.config.Previous.Listen)
deleted := util.Difference(t.link.core.config.Previous.Listen, t.link.core.config.Current.Listen)
t.link.core.config.Mutex.RUnlock()
if len(added) > 0 || len(deleted) > 0 {
for _, a := range added {
if a[:6] != "tcp://" {
continue
}
if _, err := t.listen(a[6:]); err != nil {
e <- err
}
}
for _, d := range deleted {
if d[:6] != "tcp://" {
continue
}
t.mutex.Lock()
if listener, ok := t.listeners[d[6:]]; ok {
t.mutex.Unlock()
listener.Stop <- true
} else {
t.mutex.Unlock()
}
}
}
}
func (t *tcp) listen(listenaddr string) (*TcpListener, error) { func (t *tcp) listen(listenaddr string) (*TcpListener, error) {
var err error var err error