diff --git a/go/lib/pathmgr/pathmgr.go b/go/lib/pathmgr/pathmgr.go index ce7a9ecaaf..914c3a92f1 100644 --- a/go/lib/pathmgr/pathmgr.go +++ b/go/lib/pathmgr/pathmgr.go @@ -218,8 +218,9 @@ func (r *PR) WatchFilter(src, dst *addr.ISD_AS, filter *PathPredicate) (*SyncPat // UnwatchFilter deletes a previously registered filter. func (r *PR) UnwatchFilter(src, dst *addr.ISD_AS, filter *PathPredicate) error { - // FIXME(scrye): Implement this - return common.NewCError("Function UnwatchFilter not implemented") + r.Lock() + defer r.Unlock() + return r.cache.removeWatch(src, dst, filter) } // Revoke asynchronously informs SCIOND about a revocation and flushes any diff --git a/go/sig/base/as.go b/go/sig/base/as.go index 3d0ba84f0f..0a989e9a9e 100644 --- a/go/sig/base/as.go +++ b/go/sig/base/as.go @@ -27,6 +27,7 @@ import ( "github.com/netsec-ethz/scion/go/lib/addr" "github.com/netsec-ethz/scion/go/lib/common" liblog "github.com/netsec-ethz/scion/go/lib/log" + "github.com/netsec-ethz/scion/go/sig/config" "github.com/netsec-ethz/scion/go/sig/egress" "github.com/netsec-ethz/scion/go/sig/sigcmn" "github.com/netsec-ethz/scion/go/sig/siginfo" @@ -38,16 +39,16 @@ const sigMgrTick = 10 * time.Second // ASEntry contains all of the information required to interact with a remote AS. type ASEntry struct { sync.RWMutex - log.Logger + Nets map[string]*NetEntry + Sigs *siginfo.SigMap IA *addr.ISD_AS IAString string - Nets map[string]*NetEntry - Sigs siginfo.SigMap Session *egress.Session DevName string tunLink netlink.Link tunIO io.ReadWriteCloser sigMgrStop chan struct{} + log.Logger } func newASEntry(ia *addr.ISD_AS) (*ASEntry, error) { @@ -56,34 +57,67 @@ func newASEntry(ia *addr.ISD_AS) (*ASEntry, error) { IA: ia, IAString: ia.String(), Nets: make(map[string]*NetEntry), - Sigs: make(siginfo.SigMap), + Sigs: &siginfo.SigMap{}, DevName: fmt.Sprintf("scion-%s", ia), sigMgrStop: make(chan struct{}), } var err error - if ae.Session, err = egress.NewSession(ia, 0, ae.SigMap, ae.Logger); err != nil { + if ae.Session, err = egress.NewSession(ia, 0, ae.Sigs, ae.Logger); err != nil { return nil, err } return ae, nil } -func (ae *ASEntry) setupNet() error { - var err error - ae.tunLink, ae.tunIO, err = xnet.ConnectTun(ae.DevName) - if err != nil { - return err +func (ae *ASEntry) ReloadConfig(cfg *config.ASEntry) bool { + ae.Lock() + defer ae.Unlock() + // Method calls first to prevent skips due to logical short-circuit + s := ae.addNewSIGS(cfg.Sigs) + s = ae.delOldSIGS(cfg.Sigs) && s + s = ae.addNewNets(cfg.Nets) && s + return ae.delOldNets(cfg.Nets) && s +} + +// addNewNets adds the networks in ipnets that are not currently configured. +func (ae *ASEntry) addNewNets(ipnets []*config.IPNet) bool { + s := true + for _, ipnet := range ipnets { + err := ae.addNet(ipnet.IPNet()) + if err != nil { + ae.Error("Unable to add network", "net", ipnet, "err", err) + s = false + } } - ae.Info("Network setup done") - go egress.NewDispatcher(ae.DevName, ae.tunIO, ae.Session).Run() - go ae.sigMgr() - ae.Session.Start() - return nil + return s +} + +// delOldNets deletes currently configured networks that are not in ipnets. +func (ae *ASEntry) delOldNets(ipnets []*config.IPNet) bool { + s := true +Top: + for _, ne := range ae.Nets { + for _, ipnet := range ipnets { + if ne.Net.String() == ipnet.IPNet().String() { + continue Top + } + } + err := ae.delNet(ne.Net) + if err != nil { + ae.Error("Unable to delete network", "NetEntry", ne, "err", err) + s = false + } + } + return s } // AddNet idempotently adds a network for the remote IA. func (ae *ASEntry) AddNet(ipnet *net.IPNet) error { ae.Lock() defer ae.Unlock() + return ae.addNet(ipnet) +} + +func (ae *ASEntry) addNet(ipnet *net.IPNet) error { if ae.tunLink == nil { // Ensure that the network setup is done, as otherwise route entries can't be added. if err := ae.setupNet(); err != nil { @@ -106,6 +140,12 @@ func (ae *ASEntry) AddNet(ipnet *net.IPNet) error { // DelIA removes a network for the remote IA. func (ae *ASEntry) DelNet(ipnet *net.IPNet) error { ae.Lock() + defer ae.Unlock() + return ae.delNet(ipnet) +} + +// DelIA removes a network for the remote IA. +func (ae *ASEntry) delNet(ipnet *net.IPNet) error { key := ipnet.String() ne, ok := ae.Nets[key] if !ok { @@ -113,16 +153,54 @@ func (ae *ASEntry) DelNet(ipnet *net.IPNet) error { return common.NewCError("DelNet: no network found", "ia", ae.IA, "net", ipnet) } delete(ae.Nets, key) - ae.Unlock() // Do cleanup outside the lock. ae.Info("Removed network", "net", ipnet) return ne.Cleanup() } +// addNewSIGS adds the SIGs in sigs that are not currently configured. +func (ae *ASEntry) addNewSIGS(sigs config.SIGSet) bool { + s := true + for _, sig := range sigs { + ctrlPort := int(sig.CtrlPort) + if ctrlPort == 0 { + ctrlPort = sigcmn.DefaultCtrlPort + } + encapPort := int(sig.EncapPort) + if encapPort == 0 { + encapPort = sigcmn.DefaultEncapPort + } + err := ae.AddSig(sig.Id, sig.Addr, ctrlPort, encapPort, true) + if err != nil { + ae.Error("Unable to add SIG", "sig", sig, "err", err) + s = false + } + } + return s +} + +// delOldSIGS deletes the currently configured SIGs that are not in sigs. +func (ae *ASEntry) delOldSIGS(sigs config.SIGSet) bool { + s := true + ae.Sigs.Range(func(id siginfo.SigIdType, sig *siginfo.Sig) bool { + if !sig.Static { + return true + } + if _, ok := sigs[sig.Id]; !ok { + err := ae.DelSig(sig.Id) + if err != nil { + ae.Error("Unable to delete SIG", "err", err) + s = false + } + } + return true + }) + return s +} + // AddSig idempotently adds a SIG for the remote IA. -func (ae *ASEntry) AddSig(id siginfo.SigIdType, ip net.IP, - ctrlPort, encapPort int, static bool) error { - ae.Lock() - defer ae.Unlock() +func (ae *ASEntry) AddSig(id siginfo.SigIdType, ip net.IP, ctrlPort, encapPort int, + static bool) error { + // ae.Sigs is thread safe, no master lock needed if len(id) == 0 { return common.NewCError("AddSig: SIG id empty", "ia", ae.IA) } @@ -137,46 +215,37 @@ func (ae *ASEntry) AddSig(id siginfo.SigIdType, ip net.IP, cerr := err.(*common.CError) return cerr.AddCtx(cerr.Ctx, "ia", ae.IA, "id", id) } - if _, ok := ae.Sigs[id]; ok { - // FIXME(kormat): support updating SIG entry. - return nil + if sig, ok := ae.Sigs.Load(id); ok { + sig.Host = addr.HostFromIP(ip) + sig.CtrlL4Port = ctrlPort + sig.EncapL4Port = encapPort + ae.Info("Updated SIG", "sig", sig) + } else { + sig := siginfo.NewSig(ae.IA, id, addr.HostFromIP(ip), ctrlPort, encapPort, static) + ae.Sigs.Store(id, sig) + ae.Info("Added SIG", "sig", sig) } - ae.Sigs[id] = siginfo.NewSig(ae.IA, id, addr.HostFromIP(ip), ctrlPort, encapPort, static) - ae.Info("Added SIG", "sig", ae.Sigs[id]) return nil } // DelSIG removes an SIG for the remote IA. func (ae *ASEntry) DelSig(id siginfo.SigIdType) error { - ae.Lock() - se, ok := ae.Sigs[id] + // ae.Sigs is thread safe, no master lock needed + se, ok := ae.Sigs.Load(id) if !ok { - ae.Unlock() return common.NewCError("DelSig: no SIG found", "ia", ae.IA, "id", id) } - delete(ae.Sigs, id) - ae.Unlock() // Do cleanup outside the lock. + ae.Sigs.Delete(id) ae.Info("Removed SIG", "id", id) return se.Cleanup() } -// Internal method to return a *copy* of the ASEntry's SigMap -func (ae *ASEntry) SigMap() siginfo.SigMap { - ae.Lock() - defer ae.Unlock() - smap := make(siginfo.SigMap) - for k, v := range ae.Sigs { - smap[k] = v - } - return smap -} - // manage the Sig map func (ae *ASEntry) sigMgr() { defer liblog.LogPanicAndExit() ticker := time.NewTicker(sigMgrTick) defer ticker.Stop() - log.Info("sigMgr starting") + ae.Info("sigMgr starting") Top: for { // TODO(kormat): handle adding new SIGs from discovery, and updating existing ones. @@ -184,13 +253,14 @@ Top: case <-ae.sigMgrStop: break Top case <-ticker.C: - smap := ae.SigMap() - for _, sig := range smap { + ae.Sigs.Range(func(id siginfo.SigIdType, sig *siginfo.Sig) bool { sig.ExpireFails() - } + return true + }) } } - log.Info("sigMgr stopping") + close(ae.sigMgrStop) + ae.Info("sigMgr stopping") } func (ae *ASEntry) Cleanup() error { @@ -204,12 +274,7 @@ func (ae *ASEntry) Cleanup() error { } // Clean up sessions, and associated workers. ae.cleanSessions() - for _, ne := range ae.Nets { - if err := ne.Cleanup(); err != nil { - cerr := err.(*common.CError) - ae.Error(cerr.Desc, cerr.Ctx...) - } - } + // The operating system also removes the routes when deleting the link. if err := netlink.LinkDel(ae.tunLink); err != nil { // Only return this error, as it's the only critical one. return common.NewCError("Error removing TUN link", @@ -223,3 +288,16 @@ func (ae *ASEntry) cleanSessions() { ae.Session.Error("Error cleaning up session", "err", err) } } + +func (ae *ASEntry) setupNet() error { + var err error + ae.tunLink, ae.tunIO, err = xnet.ConnectTun(ae.DevName) + if err != nil { + return err + } + ae.Info("Network setup done") + go egress.NewDispatcher(ae.DevName, ae.tunIO, ae.Session).Run() + go ae.sigMgr() + ae.Session.Start() + return nil +} diff --git a/go/sig/base/map.go b/go/sig/base/map.go index dc1f890c46..4b439d44f1 100644 --- a/go/sig/base/map.go +++ b/go/sig/base/map.go @@ -22,19 +22,93 @@ import ( "github.com/netsec-ethz/scion/go/lib/addr" "github.com/netsec-ethz/scion/go/lib/common" + "github.com/netsec-ethz/scion/go/sig/config" ) var Map = newASMap() -// ASMap is a RWMutex-protected map of ASEntries. -type ASMap struct { - // FIXME(kormat): when we switch to go 1.9, consider replacing this with sync.Map. - sync.RWMutex - t map[addr.IAInt]*ASEntry -} +// ASMap is not concurrency safe against multiple writers. +type ASMap sync.Map func newASMap() *ASMap { - return &ASMap{t: make(map[addr.IAInt]*ASEntry)} + return &ASMap{} +} + +func (am *ASMap) Delete(key addr.IAInt) { + (*sync.Map)(am).Delete(key) +} + +func (am *ASMap) Load(key addr.IAInt) (*ASEntry, bool) { + value, ok := (*sync.Map)(am).Load(key) + if value == nil { + return nil, ok + } + return value.(*ASEntry), ok +} + +func (am *ASMap) LoadOrStore(key addr.IAInt, value *ASEntry) (*ASEntry, bool) { + actual, ok := (*sync.Map)(am).LoadOrStore(key, value) + if actual == nil { + return nil, ok + } + return actual.(*ASEntry), ok +} + +func (am *ASMap) Store(key addr.IAInt, value *ASEntry) { + (*sync.Map)(am).Store(key, value) +} + +func (am *ASMap) Range(f func(key addr.IAInt, value *ASEntry) bool) { + (*sync.Map)(am).Range(func(key, value interface{}) bool { + return f(key.(addr.IAInt), value.(*ASEntry)) + }) +} + +func (am *ASMap) ReloadConfig(cfg *config.Cfg) bool { + // Method calls first to prevent skips due to logical short-circuit + s := am.addNewIAs(cfg) + return am.delOldIAs(cfg) && s +} + +// addNewIAs adds the ASes in cfg that are not currently configured. +func (am *ASMap) addNewIAs(cfg *config.Cfg) bool { + s := true + for iaVal, cfgEntry := range cfg.ASes { + ia := &iaVal + log.Info("ReloadConfig: Adding AS...", "ia", ia) + ae, err := am.AddIA(ia) + if err != nil { + cerr := err.(*common.CError) + log.Error(cerr.Desc, cerr.Ctx...) + s = false + continue + } + s = ae.ReloadConfig(cfgEntry) && s + log.Info("ReloadConfig: Added AS", "ia", ia) + } + return s +} + +func (am *ASMap) delOldIAs(cfg *config.Cfg) bool { + s := true + // Delete all ASes that currently exist but are not in cfg + am.Range(func(iaInt addr.IAInt, as *ASEntry) bool { + ia := iaInt.IA() + if _, ok := cfg.ASes[*ia]; !ok { + log.Info("ReloadConfig: Deleting AS...", "ia", ia) + // Deletion also handles session/tun device cleanup + err := am.DelIA(ia) + if err != nil { + cerr := err.(*common.CError) + log.Error(cerr.Desc, cerr.Ctx...) + s = false + return true + } + log.Info("ReloadConfig: Deleted AS", "ia", ia) + } + return true + }) + return s } // AddIA idempotently adds an entry for a remote IA. @@ -43,10 +117,8 @@ func (am *ASMap) AddIA(ia *addr.ISD_AS) (*ASEntry, error) { // A 0 for either ISD or AS indicates a wildcard, and not a specific ISD-AS. return nil, common.NewCError("AddIA: ISD and AS must not be 0", "ia", ia) } - am.Lock() - defer am.Unlock() key := ia.IAInt() - ae, ok := am.t[key] + ae, ok := am.Load(key) if ok { return ae, nil } @@ -54,29 +126,25 @@ func (am *ASMap) AddIA(ia *addr.ISD_AS) (*ASEntry, error) { if err != nil { return nil, err } - am.t[key] = ae - log.Info("Added IA", "ia", ia) + am.Store(key, ae) return ae, nil } // DelIA removes an entry for a remote IA. func (am *ASMap) DelIA(ia *addr.ISD_AS) error { - am.Lock() key := ia.IAInt() - ae, ok := am.t[key] + ae, ok := am.Load(key) if !ok { - am.Unlock() return common.NewCError("DelIA: No entry found", "ia", ia) } - delete(am.t, key) - am.Unlock() // Do cleanup outside the lock. - log.Info("Removed IA", "ia", ia) + am.Delete(key) return ae.Cleanup() } // ASEntry returns the entry for the specified remote IA, or nil if not present. func (am *ASMap) ASEntry(ia *addr.ISD_AS) *ASEntry { - am.RLock() - defer am.RUnlock() - return am.t[ia.IAInt()] + if as, ok := am.Load(ia.IAInt()); ok { + return as + } + return nil } diff --git a/go/sig/base/nets.go b/go/sig/base/nets.go index 5b7812f3e2..d0dc1e4491 100644 --- a/go/sig/base/nets.go +++ b/go/sig/base/nets.go @@ -43,7 +43,7 @@ func (ne *NetEntry) setup() error { func (ne *NetEntry) Cleanup() error { if err := netlink.RouteDel(ne.Route); err != nil { - return common.NewCError("Unable to add route for remote network", + return common.NewCError("Unable to delete route for remote network", "route", ne.Route, "err", err) } return nil diff --git a/go/sig/config/config.go b/go/sig/config/config.go index c8aa4fd0da..ee310c33e7 100644 --- a/go/sig/config/config.go +++ b/go/sig/config/config.go @@ -21,8 +21,6 @@ import ( "io/ioutil" "net" - //log "github.com/inconshreveable/log15" - "github.com/netsec-ethz/scion/go/lib/addr" "github.com/netsec-ethz/scion/go/lib/common" "github.com/netsec-ethz/scion/go/sig/siginfo" @@ -55,12 +53,19 @@ func Parse(b common.RawBytes) (*Cfg, error) { if err := json.Unmarshal(b, cfg); err != nil { return nil, common.NewCError("Unable to parse SIG config", "err", err) } + // Populate IDs + for _, as := range cfg.ASes { + for id := range as.Sigs { + sig := as.Sigs[id] + sig.Id = id + } + } return cfg, nil } type ASEntry struct { Nets []*IPNet - Sigs map[siginfo.SigIdType]*SIG + Sigs SIGSet } // IPNet is custom type of net.IPNet, to allow custom unmarshalling. @@ -90,3 +95,5 @@ type SIG struct { CtrlPort uint16 EncapPort uint16 } + +type SIGSet map[siginfo.SigIdType]*SIG diff --git a/go/sig/egress/dispatcher.go b/go/sig/egress/dispatcher.go index 6a9e435250..66519e2bf1 100644 --- a/go/sig/egress/dispatcher.go +++ b/go/sig/egress/dispatcher.go @@ -16,6 +16,7 @@ package egress import ( "io" + "os" log "github.com/inconshreveable/log15" "github.com/prometheus/client_golang/prometheus" @@ -82,6 +83,13 @@ BatchLoop: // This dispatcher is shutting down break BatchLoop } + // Sometimes we don't receive a clean EOF, so we check if the + // tunnel device is closed. + if pErr, ok := err.(*os.PathError); ok { + if pErr.Err == os.ErrClosed { + break BatchLoop + } + } ed.Error("EgressDispatcher: error reading from devIO", "err", err) continue } diff --git a/go/sig/egress/session.go b/go/sig/egress/session.go index a9b7b8d328..9bc5bb5cbe 100644 --- a/go/sig/egress/session.go +++ b/go/sig/egress/session.go @@ -22,6 +22,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/netsec-ethz/scion/go/lib/addr" + "github.com/netsec-ethz/scion/go/lib/common" "github.com/netsec-ethz/scion/go/lib/pathmgr" "github.com/netsec-ethz/scion/go/lib/pktdisp" "github.com/netsec-ethz/scion/go/lib/ringbuf" @@ -38,8 +39,8 @@ type Session struct { SessId sigcmn.SessionType // pool of paths, managed by pathmgr pool *pathmgr.SyncPaths - // function pointer to return SigMap from parent ASEntry. - sigMapF func() siginfo.SigMap + // remote SIGs + sigMap *siginfo.SigMap // *RemoteInfo currRemote atomic.Value // bool @@ -52,13 +53,13 @@ type Session struct { } func NewSession(dstIA *addr.ISD_AS, sessId sigcmn.SessionType, - sigMapF func() siginfo.SigMap, logger log.Logger) (*Session, error) { + sigMap *siginfo.SigMap, logger log.Logger) (*Session, error) { var err error s := &Session{ - Logger: logger.New("sessId", sessId), - IA: dstIA, - SessId: sessId, - sigMapF: sigMapF, + Logger: logger.New("sessId", sessId), + IA: dstIA, + SessId: sessId, + sigMap: sigMap, } if s.pool, err = sigcmn.PathMgr.Watch(sigcmn.IA, s.IA); err != nil { return nil, err @@ -90,7 +91,14 @@ func (s *Session) Cleanup() error { s.Debug("egress.Session Cleanup: wait for session monitor") <-s.sessMonStopped s.Debug("egress.Session Cleanup: closing conn") - return s.conn.Close() + if err := s.conn.Close(); err != nil { + return common.NewCError("Unable to close conn", "err", err) + } + if err := sigcmn.PathMgr.Unwatch(sigcmn.IA, s.IA); err != nil { + return common.NewCError("Unable to unwatch src-dst", "src", sigcmn.IA, "dst", s.IA, + "err", err) + } + return nil } func (s *Session) Remote() *RemoteInfo { diff --git a/go/sig/egress/sessmon.go b/go/sig/egress/sessmon.go index c326a833a6..6b5786e633 100644 --- a/go/sig/egress/sessmon.go +++ b/go/sig/egress/sessmon.go @@ -41,8 +41,6 @@ type sessMonitor struct { log.Logger // the Session this instance is monitoring. sess *Session - // the current map of SIGs for the remote AS - sigMap siginfo.SigMap // the (filtered) pool of paths to the remote AS, maintained by pathmgr. pool *pathmgr.SyncPaths // the pool of paths this session is currently using, frequently refreshed from pool. @@ -80,7 +78,6 @@ func (sm *sessMonitor) run() { disp.Dispatcher.Register(disp.RegPollRep, disp.MkRegPollKey(sm.sess.IA, sm.sess.SessId), regc) sm.lastReply = time.Now() sm.Info("sessMonitor: starting") - defer sm.Info("sessMonitor: stopped") Top: for { select { @@ -89,13 +86,18 @@ Top: case <-reqTick.C: // Update paths and sigs sm.sessPathPool.update(sm.pool.Load().APS) - sm.sigMap = sm.sess.sigMapF() sm.updateRemote() sm.sendReq() case rpld := <-regc: sm.handleRep(rpld) } } + err := disp.Dispatcher.Unregister(disp.RegPollRep, disp.MkRegPollKey(sm.sess.IA, + sm.sess.SessId)) + if err != nil { + log.Error("sessMonitor: unable to unregister from ctrl dispatcher", "err", err) + } + sm.Info("sessMonitor: stopped") } func (sm *sessMonitor) updateRemote() { @@ -127,7 +129,7 @@ func (sm *sessMonitor) updateRemote() { sm.Debug("No remote SIG", "remote", currRemote) currSig = sm.getNewSig(nil) sm.needUpdate = true - } else if _, ok := sm.sigMap[currSig.Id]; !ok { + } else if _, ok := sm.sess.sigMap.Load(currSig.Id); !ok { // Current SIG is no longer listed, need to switch to a new one. sm.Debug("Current SIG invalid", "remote", currRemote) currSig = sm.getNewSig(nil) @@ -151,12 +153,12 @@ func (sm *sessMonitor) updateRemote() { func (sm *sessMonitor) getNewSig(old *siginfo.Sig) *siginfo.Sig { if old != nil { // Try to get a different SIG, if possible. - if sig := sm.sigMap.GetSig(old.Id); sig != nil { + if sig := sm.sess.sigMap.GetSig(old.Id); sig != nil { return sig } } // Get SIG with lowest failure count. - return sm.sigMap.GetSig("") + return sm.sess.sigMap.GetSig("") } func (sm *sessMonitor) getNewPath(old *sessPath) *sessPath { diff --git a/go/sig/sig.go b/go/sig/sig.go index 3d0af0d756..35f1df8e29 100644 --- a/go/sig/sig.go +++ b/go/sig/sig.go @@ -36,6 +36,13 @@ import ( "github.com/netsec-ethz/scion/go/sig/sigcmn" ) +var sighup chan os.Signal + +func init() { + sighup = make(chan os.Signal, 1) + signal.Notify(sighup, syscall.SIGHUP) +} + var ( id = flag.String("id", "", "Element ID (Required. E.g. 'sig4-21-9')") cfgPath = flag.String("config", "", "Config file (Required)") @@ -74,10 +81,12 @@ func main() { egress.Init() disp.Init(sigcmn.CtrlConn) go base.PollReqHdlr() + // Parse config if loadConfig(*cfgPath) != true { fatal("Unable to load config on startup") } + go reloadOnSIGHUP(*cfgPath) // Spawn ingress Dispatcher. if err := ingress.Init(); err != nil { @@ -97,6 +106,18 @@ func setupSignals() { }() } +func reloadOnSIGHUP(path string) { + defer liblog.LogPanicAndExit() + log.Info("reloadOnSIGHUP: started") + for range sighup { + log.Info("reloadOnSIGHUP: reloading...") + success := loadConfig(path) + // Errors already logged in loadConfig + log.Info("reloadOnSIGHUP: reload done", "success", success) + } + log.Info("reloadOnSIGHUP: stopped") +} + func loadConfig(path string) bool { cfg, err := config.LoadFromFile(path) if err != nil { @@ -104,43 +125,7 @@ func loadConfig(path string) bool { log.Error(cerr.Desc, cerr.Ctx...) return false } - success := true - for iaVal, cfgEntry := range cfg.ASes { - ia := &iaVal - ae, err := base.Map.AddIA(ia) - if err != nil { - cerr := err.(*common.CError) - log.Error(cerr.Desc, cerr.Ctx...) - success = false - continue - } - // Add sigs before networks, so there's somewhere for packets to go. - for id, sig := range cfgEntry.Sigs { - ctrlPort := int(sig.CtrlPort) - if ctrlPort == 0 { - ctrlPort = sigcmn.DefaultCtrlPort - } - encapPort := int(sig.EncapPort) - if encapPort == 0 { - encapPort = sigcmn.DefaultEncapPort - } - if err := ae.AddSig(id, sig.Addr, ctrlPort, encapPort, true); err != nil { - cerr := err.(*common.CError) - log.Error(cerr.Desc, cerr.Ctx...) - success = false - continue - } - } - for _, netw := range cfgEntry.Nets { - if err := ae.AddNet(netw.IPNet()); err != nil { - cerr := err.(*common.CError) - log.Error(cerr.Desc, cerr.Ctx...) - success = false - continue - } - } - } - return success + return base.Map.ReloadConfig(cfg) } func fatal(msg string, args ...interface{}) { diff --git a/go/sig/siginfo/sig.go b/go/sig/siginfo/sig.go index b7d17bde51..9ac554da63 100644 --- a/go/sig/siginfo/sig.go +++ b/go/sig/siginfo/sig.go @@ -32,23 +32,55 @@ const ( ) type SigIdType string -type SigMap map[SigIdType]*Sig + +type SigMap sync.Map + +func (sm *SigMap) Delete(key SigIdType) { + (*sync.Map)(sm).Delete(key) +} + +func (sm *SigMap) Load(key SigIdType) (*Sig, bool) { + value, ok := (*sync.Map)(sm).Load(key) + if value == nil { + return nil, ok + } + return value.(*Sig), ok +} + +func (sm *SigMap) LoadOrStore(key SigIdType, value *Sig) (*Sig, bool) { + actual, ok := (*sync.Map)(sm).LoadOrStore(key, value) + if actual == nil { + return nil, ok + } + return actual.(*Sig), ok +} + +func (sm *SigMap) Store(key SigIdType, value *Sig) { + (*sync.Map)(sm).Store(key, value) +} + +func (sm *SigMap) Range(f func(key SigIdType, value *Sig) bool) { + (*sync.Map)(sm).Range(func(key, value interface{}) bool { + return f(key.(SigIdType), value.(*Sig)) + }) +} // return the Sig with the lowest fail count. -func (sm SigMap) GetSig(currSigId SigIdType) *Sig { +func (sm *SigMap) GetSig(currSigId SigIdType) *Sig { var s *Sig var minFail uint16 = math.MaxUint16 - for id, sig := range sm { + sm.Range(func(id SigIdType, sig *Sig) bool { if id == currSigId { // If a current Sig ID is supplied, don't reply with the same one. - continue + return true } failCount := sig.FailCount() if failCount < minFail { s = sig minFail = failCount } - } + return true + }) return s }