From 7e9fe4ff985475ed42f42638199a1ebe36c88251 Mon Sep 17 00:00:00 2001 From: Josh Deprez Date: Sun, 5 May 2024 17:01:23 +1000 Subject: [PATCH] Unify route and zone tables --- main.go | 37 ++++----- router/nbp.go | 8 +- router/peer_aurp.go | 27 +++--- router/route.go | 18 ++-- router/router.go | 1 - router/zip.go | 4 +- router/zones.go | 196 +++++++++++++++----------------------------- 7 files changed, 112 insertions(+), 179 deletions(-) diff --git a/main.go b/main.go index 59932e7..ff6809c 100644 --- a/main.go +++ b/main.go @@ -50,6 +50,7 @@ const routingTableTemplate = ` Network range Extended? + Zone names Distance Last seen Port @@ -58,7 +59,8 @@ const routingTableTemplate = ` {{range $route := . }} {{$route.NetStart}}{{if not (eq $route.NetStart $route.NetEnd)}} - {{$route.NetEnd}}{{end}} - {{if $route.Extended}}✅{{else}}❌{{end}} + {{if $route.Extended}}✅{{else}}-{{end}} + {{range $route.ZoneNames}}{{.}}
{{end}} {{$route.Distance}} {{$route.LastSeenAgo}} @@ -220,14 +222,14 @@ func main() { return rs, nil }) - zones := router.NewZoneTable() - status.AddItem(ctx, "Zone table", zoneTableTemplate, func(context.Context) (any, error) { - zs := zones.Dump() - slices.SortFunc(zs, func(za, zb router.Zone) int { - return cmp.Compare(za.Name, zb.Name) - }) - return zs, nil - }) + // zones := router.NewZoneTable() + // status.AddItem(ctx, "Zone table", zoneTableTemplate, func(context.Context) (any, error) { + // zs := zones.Dump() + // slices.SortFunc(zs, func(za, zb router.Zone) int { + // return cmp.Compare(za.Name, zb.Name) + // }) + // return zs, nil + // }) // -------------------------------- Peers --------------------------------- var peersMu sync.Mutex @@ -326,8 +328,7 @@ func main() { ConfiguredAddr: peerStr, RemoteAddr: raddr, ReceiveCh: make(chan aurp.Packet, 1024), - RoutingTable: routes, - ZoneTable: zones, + RouteTable: routes, } aurp.Inc(&nextConnID) peersMu.Lock() @@ -344,7 +345,7 @@ func main() { rooter := &router.Router{ Config: cfg, RouteTable: routes, - ZoneTable: zones, + // ZoneTable: zones, } etherTalkPort := &router.EtherTalkPort{ @@ -360,9 +361,6 @@ func main() { } rooter.Ports = append(rooter.Ports, etherTalkPort) routes.InsertEtherTalkDirect(etherTalkPort) - for _, az := range etherTalkPort.AvailableZones { - zones.Upsert(etherTalkPort.NetStart, az, etherTalkPort) - } // --------------------------------- RTMP --------------------------------- go etherTalkPort.RunRTMP(ctx) @@ -434,11 +432,10 @@ func main() { RemoteDI: dh.SourceDI, // platinum rule LocalConnID: nextConnID, }, - UDPConn: ln, - RemoteAddr: raddr, - ReceiveCh: make(chan aurp.Packet, 1024), - RoutingTable: routes, - ZoneTable: zones, + UDPConn: ln, + RemoteAddr: raddr, + ReceiveCh: make(chan aurp.Packet, 1024), + RouteTable: routes, } aurp.Inc(&nextConnID) peers[ra] = pr diff --git a/router/nbp.go b/router/nbp.go index 673b9d3..575b9f1 100644 --- a/router/nbp.go +++ b/router/nbp.go @@ -74,10 +74,10 @@ func (port *EtherTalkPort) handleNBPBrRq(ctx context.Context, ddpkt *ddp.ExtPack // tuple.Zone = port.DefaultZoneName // } - zones := port.Router.ZoneTable.LookupName(tuple.Zone) + routes := port.Router.RouteTable.RoutesForZone(tuple.Zone) - for _, z := range zones { - if outPort := z.LocalPort; outPort != nil { + for _, route := range routes { + if outPort := route.EtherTalkDirect; outPort != nil { // If it's for a local zone, translate it to a LkUp and broadcast // out the corresponding EtherTalk port. // "Note: On an internet, nodes on extended networks performing lookups in @@ -147,7 +147,7 @@ func (port *EtherTalkPort) handleNBPBrRq(ctx context.Context, ddpkt *ddp.ExtPack SrcNet: ddpkt.SrcNet, SrcNode: ddpkt.SrcNode, SrcSocket: ddpkt.SrcSocket, - DstNet: z.Network, + DstNet: route.NetStart, DstNode: 0x00, // Any router for the dest network DstSocket: 2, Proto: ddp.ProtoNBP, diff --git a/router/peer_aurp.go b/router/peer_aurp.go index 85432cc..0aca253 100644 --- a/router/peer_aurp.go +++ b/router/peer_aurp.go @@ -114,11 +114,8 @@ type AURPPeer struct { // Incoming packet channel. ReceiveCh chan aurp.Packet - // Routing table (the peer will add/remove/update routes) - RoutingTable *RouteTable - - // Zone table (the peer will add/remove/update zones) - ZoneTable *ZoneTable + // Route table (the peer will add/remove/update routes and zones) + RouteTable *RouteTable mu sync.RWMutex rstate ReceiverState @@ -253,7 +250,7 @@ func (p *AURPPeer) Handle(ctx context.Context) error { if sendRetries >= tickleRetryLimit { log.Printf("AURP Peer: Send retry limit reached while waiting for Tickle-Ack, closing connection") p.setRState(ReceiverUnconnected) - p.RoutingTable.DeleteAURPPeer(p) + p.RouteTable.DeleteAURPPeer(p) break } @@ -272,7 +269,7 @@ func (p *AURPPeer) Handle(ctx context.Context) error { if sendRetries >= sendRetryLimit { log.Printf("AURP Peer: Send retry limit reached while waiting for RI-Rsp, closing connection") p.setRState(ReceiverUnconnected) - p.RoutingTable.DeleteAURPPeer(p) + p.RouteTable.DeleteAURPPeer(p) break } @@ -467,7 +464,7 @@ func (p *AURPPeer) Handle(ctx context.Context) error { log.Printf("AURP Peer: Learned about these networks: %v", pkt.Networks) for _, nt := range pkt.Networks { - p.RoutingTable.InsertAURPRoute( + p.RouteTable.InsertAURPRoute( p, nt.Extended, ddp.Network(nt.RangeStart), @@ -543,7 +540,7 @@ func (p *AURPPeer) Handle(ctx context.Context) error { // Do nothing except respond with RI-Ack case aurp.EventCodeNA: - if err := p.RoutingTable.InsertAURPRoute( + if err := p.RouteTable.InsertAURPRoute( p, et.Extended, et.RangeStart, @@ -555,10 +552,10 @@ func (p *AURPPeer) Handle(ctx context.Context) error { ackFlag = aurp.RoutingFlagSendZoneInfo case aurp.EventCodeND: - p.RoutingTable.DeleteAURPPeerNetwork(p, et.RangeStart) + p.RouteTable.DeleteAURPPeerNetwork(p, et.RangeStart) case aurp.EventCodeNDC: - p.RoutingTable.UpdateAURPRouteDistance(p, et.RangeStart, et.Distance+1) + p.RouteTable.UpdateAURPRouteDistance(p, et.RangeStart, et.Distance+1) case aurp.EventCodeNRC: // "An exterior router sends a Network Route Change @@ -566,7 +563,7 @@ func (p *AURPPeer) Handle(ctx context.Context) error { // through its local internet changes to a path through // a tunneling port, causing split-horizoned processing // to eliminate that network’s routing information." - p.RoutingTable.DeleteAURPPeerNetwork(p, et.RangeStart) + p.RouteTable.DeleteAURPPeerNetwork(p, et.RangeStart) case aurp.EventCodeZC: // "This event is reserved for future use." @@ -584,7 +581,7 @@ func (p *AURPPeer) Handle(ctx context.Context) error { } log.Printf("AURP Peer: Router Down: error code %d %s", pkt.ErrorCode, pkt.ErrorCode) - p.RoutingTable.DeleteAURPPeer(p) + p.RouteTable.DeleteAURPPeer(p) // Respond with RI-Ack if _, err := p.Send(p.Transport.NewRIAckPacket(pkt.ConnectionID, pkt.Sequence, 0)); err != nil { @@ -596,7 +593,7 @@ func (p *AURPPeer) Handle(ctx context.Context) error { case *aurp.ZIReqPacket: // TODO: split ZI-Rsp packets similarly to ZIP Replies - zones := p.ZoneTable.Query(pkt.Networks) + zones := p.RouteTable.ZonesForNetworks(pkt.Networks) if _, err := p.Send(p.Transport.NewZIRspPacket(zones)); err != nil { log.Printf("AURP Peer: Couldn't send ZI-Rsp packet: %v", err) return err @@ -605,7 +602,7 @@ func (p *AURPPeer) Handle(ctx context.Context) error { case *aurp.ZIRspPacket: log.Printf("AURP Peer: Learned about these zones: %v", pkt.Zones) for _, zt := range pkt.Zones { - p.ZoneTable.Upsert(ddp.Network(zt.Network), zt.Name, nil) + p.RouteTable.AddZoneToNetwork(zt.Network, zt.Name) } case *aurp.GDZLReqPacket: diff --git a/router/route.go b/router/route.go index a399836..1042a34 100644 --- a/router/route.go +++ b/router/route.go @@ -34,6 +34,10 @@ type Route struct { LastSeen time.Time + // ZoneNames may be empty between learning the existence of a route and + // receiving zone information. + ZoneNames []string + // Exactly one of the following should be set AURPPeer *AURPPeer // Next hop is this peer router (over AURP) EtherTalkPeer *EtherTalkPeer // Next hop is this peer router (over EtherTalk) @@ -47,6 +51,10 @@ func (r Route) LastSeenAgo() string { return fmt.Sprintf("%v ago", time.Since(r.LastSeen).Truncate(time.Millisecond)) } +func (r *Route) Valid() bool { + return r.EtherTalkPeer == nil || time.Since(r.LastSeen) <= maxRouteAge +} + type RouteTable struct { mu sync.Mutex routes map[*Route]struct{} @@ -65,6 +73,7 @@ func (rt *RouteTable) InsertEtherTalkDirect(port *EtherTalkPort) { NetEnd: port.NetEnd, Distance: 0, // we're connected directly LastSeen: time.Now(), + ZoneNames: port.AvailableZones, EtherTalkDirect: port, } @@ -93,8 +102,7 @@ func (rt *RouteTable) LookupRoute(network ddp.Network) *Route { if network < r.NetStart || network > r.NetEnd { continue } - // Exclude EtherTalk routes that are too old - if r.EtherTalkPeer != nil && time.Since(r.LastSeen) > maxRouteAge { + if !r.Valid() { continue } if bestRoute == nil { @@ -213,11 +221,9 @@ func (rt *RouteTable) ValidRoutes() []*Route { defer rt.mu.Unlock() valid := make([]*Route, 0, len(rt.routes)) for r := range rt.routes { - // Exclude EtherTalk routes that are too old - if r.EtherTalkPeer != nil && time.Since(r.LastSeen) > maxRouteAge { - continue + if r.Valid() { + valid = append(valid, r) } - valid = append(valid, r) } return valid } diff --git a/router/router.go b/router/router.go index 7295076..76f2fab 100644 --- a/router/router.go +++ b/router/router.go @@ -26,7 +26,6 @@ import ( type Router struct { Config *Config RouteTable *RouteTable - ZoneTable *ZoneTable Ports []*EtherTalkPort } diff --git a/router/zip.go b/router/zip.go index 669e1f4..ed52f5b 100644 --- a/router/zip.go +++ b/router/zip.go @@ -62,7 +62,7 @@ func (port *EtherTalkPort) handleZIPZIP(ctx context.Context, ddpkt *ddp.ExtPacke func (port *EtherTalkPort) handleZIPQuery(ctx context.Context, ddpkt *ddp.ExtPacket, zipkt *zip.QueryPacket) error { log.Printf("ZIP: Got Query for networks %v", zipkt.Networks) - networks := port.Router.ZoneTable.Query(zipkt.Networks) + networks := port.Router.RouteTable.ZonesForNetworks(zipkt.Networks) sendReply := func(resp *zip.ReplyPacket) error { respRaw, err := resp.Marshal() @@ -258,7 +258,7 @@ func (port *EtherTalkPort) handleZIPTReq(ctx context.Context, ddpkt *ddp.ExtPack switch gzl.Function { case zip.FunctionGetZoneList: - resp.Zones = port.Router.ZoneTable.AllNames() + resp.Zones = port.Router.RouteTable.AllZoneNames() case zip.FunctionGetLocalZones: resp.Zones = port.AvailableZones diff --git a/router/zones.go b/router/zones.go index f1d0d74..5f8ca75 100644 --- a/router/zones.go +++ b/router/zones.go @@ -17,145 +17,79 @@ package router import ( - "fmt" "slices" - "sort" - "sync" - "time" "github.com/sfiera/multitalk/pkg/ddp" ) -//const maxZoneAge = 10 * time.Minute // TODO: confirm - -type Zone struct { - Network ddp.Network - Name string - LocalPort *EtherTalkPort // nil if remote (local to another router) - LastSeen time.Time -} - -func (z Zone) LastSeenAgo() string { - if z.LastSeen.IsZero() { - return "never" - } - return fmt.Sprintf("%v ago", time.Since(z.LastSeen).Truncate(time.Millisecond)) -} - -type zoneKey struct { - network ddp.Network - name string -} - -type ZoneTable struct { - mu sync.Mutex - zones map[zoneKey]*Zone -} - -func NewZoneTable() *ZoneTable { - return &ZoneTable{ - zones: make(map[zoneKey]*Zone), - } -} - -func (zt *ZoneTable) Dump() []Zone { - zt.mu.Lock() - defer zt.mu.Unlock() - zs := make([]Zone, 0, len(zt.zones)) - for _, z := range zt.zones { - zs = append(zs, *z) - } - return zs -} - -func (zt *ZoneTable) Upsert(network ddp.Network, name string, localPort *EtherTalkPort) { - zt.mu.Lock() - defer zt.mu.Unlock() - key := zoneKey{network, name} - z := zt.zones[key] - if z != nil { - z.LocalPort = localPort - z.LastSeen = time.Now() - return - } - zt.zones[key] = &Zone{ - Network: network, - Name: name, - LocalPort: localPort, - LastSeen: time.Now(), - } -} - -func (zt *ZoneTable) Query(ns []ddp.Network) map[ddp.Network][]string { - slices.Sort(ns) - zs := make(map[ddp.Network][]string) - - zt.mu.Lock() - defer zt.mu.Unlock() - for _, z := range zt.zones { - // if time.Since(z.LastSeen) > maxZoneAge { - // continue - // } - if _, ok := slices.BinarySearch(ns, z.Network); ok { - zs[z.Network] = append(zs[z.Network], z.Name) - } - } - return zs -} - -func (zt *ZoneTable) LookupName(name string) []*Zone { - zt.mu.Lock() - defer zt.mu.Unlock() - - var zs []*Zone - for _, z := range zt.zones { - if z.Name == name { - zs = append(zs, z) - } - } - return zs -} - -// func (zt *ZoneTable) LocalNames() []string { -// zt.mu.Lock() -// seen := make(map[string]struct{}) -// zs := make([]string, 0, len(zt.zones)) -// for _, z := range zt.zones { -// // if time.Since(z.LastSeen) > maxZoneAge { -// // continue -// // } -// if z.Local != nil { -// continue -// } -// if _, s := seen[z.Name]; s { -// continue -// } -// seen[z.Name] = struct{}{} -// zs = append(zs, z.Name) - -// } -// zt.mu.Unlock() - -// sort.Strings(zs) -// return zs -// } - -func (zt *ZoneTable) AllNames() []string { - zt.mu.Lock() - seen := make(map[string]struct{}) - zs := make([]string, 0, len(zt.zones)) - for _, z := range zt.zones { - // if time.Since(z.LastSeen) > maxZoneAge { - // continue - // } - if _, s := seen[z.Name]; s { +func (rt *RouteTable) AddZoneToNetwork(n ddp.Network, z string) { + rt.mu.Lock() + defer rt.mu.Unlock() + for r := range rt.routes { + if n < r.NetStart || n > r.NetEnd { continue } - seen[z.Name] = struct{}{} - zs = append(zs, z.Name) + if !r.Valid() { + continue + } + if slices.Contains(r.ZoneNames, z) { + continue + } + r.ZoneNames = append(r.ZoneNames, z) } - zt.mu.Unlock() +} - sort.Strings(zs) +func (rt *RouteTable) ZonesForNetworks(ns []ddp.Network) map[ddp.Network][]string { + zs := make(map[ddp.Network][]string) + + rt.mu.Lock() + defer rt.mu.Unlock() + for r := range rt.routes { + if !r.Valid() { + continue + } + if _, ok := slices.BinarySearch(ns, r.NetStart); ok { + zs[r.NetStart] = append(zs[r.NetStart], r.ZoneNames...) + } + } return zs } + +func (rt *RouteTable) RoutesForZone(zone string) []*Route { + rt.mu.Lock() + defer rt.mu.Unlock() + + var routes []*Route + for r := range rt.routes { + if !r.Valid() { + continue + } + if slices.Contains(r.ZoneNames, zone) { + routes = append(routes, r) + } + } + return routes +} + +func (rt *RouteTable) AllZoneNames() (zones []string) { + defer slices.Sort(zones) + + rt.mu.Lock() + defer rt.mu.Unlock() + + seen := make(map[string]struct{}) + for r := range rt.routes { + if !r.Valid() { + continue + } + for _, z := range r.ZoneNames { + if _, s := seen[z]; s { + continue + } + seen[z] = struct{}{} + zones = append(zones, z) + } + } + + return zones +}