diff --git a/amt.go b/amt.go new file mode 100644 index 0000000..b9d545c --- /dev/null +++ b/amt.go @@ -0,0 +1,46 @@ +package main + +import ( + "sync" + "time" + + "github.com/sfiera/multitalk/pkg/ddp" + "github.com/sfiera/multitalk/pkg/ethernet" +) + +// TODO: verify this parameter +const maxAMTEntryAge = 30 * time.Second + +type amtEntry struct { + hwAddr ethernet.Addr + last time.Time +} + +// AMT implements a concurrent-safe Address Mapping Table for AppleTalk (DDP) +// addresses to Ethernet hardware addresses. +type AMT struct { + mu sync.RWMutex + table map[ddp.Addr]amtEntry +} + +// Learn adds or updates an AMT entry. +func (t *AMT) Learn(ddpAddr ddp.Addr, hwAddr ethernet.Addr) { + t.mu.Lock() + defer t.mu.Unlock() + if t.table == nil { + t.table = make(map[ddp.Addr]amtEntry) + } + t.table[ddpAddr] = amtEntry{ + hwAddr: hwAddr, + last: time.Now(), + } +} + +// Lookup searches for a non-expired entry in the table only. It does not send +// any packets. +func (t *AMT) Lookup(ddpAddr ddp.Addr) (ethernet.Addr, bool) { + t.mu.RLock() + defer t.mu.RUnlock() + ent, ok := t.table[ddpAddr] + return ent.hwAddr, ok && time.Since(ent.last) < maxAMTEntryAge +} diff --git a/main.go b/main.go index a87f661..e20e679 100644 --- a/main.go +++ b/main.go @@ -34,7 +34,6 @@ import ( "gitea.drjosh.dev/josh/jrouter/aurp" "github.com/sfiera/multitalk/pkg/aarp" "github.com/sfiera/multitalk/pkg/ddp" - "github.com/sfiera/multitalk/pkg/ethernet" "github.com/sfiera/multitalk/pkg/ethertalk" ) @@ -140,11 +139,7 @@ func main() { } // AppleTalk packet loop - type amtEntry struct { - hwAddr ethernet.Addr - last time.Time - } - amt := make(map[ddp.Addr]amtEntry) + var amt AMT go func() { iface, err := net.InterfaceByName(cfg.EtherTalk.Device) if err != nil { @@ -186,18 +181,12 @@ func main() { case aarp.RequestOp: log.Printf("AARP: Who has %v? Tell %v", aapkt.Dst.Proto, aapkt.Src.Proto) // Glean that aapkt.Src.Proto -> aapkt.Src.Hardware - amt[aapkt.Src.Proto] = amtEntry{ - hwAddr: aapkt.Src.Hardware, - last: time.Now(), - } + amt.Learn(aapkt.Src.Proto, aapkt.Src.Hardware) log.Printf("AARP: Gleaned that %v -> %v", aapkt.Src.Proto, aapkt.Src.Hardware) case aarp.ResponseOp: log.Printf("AARP: %v is at %v", aapkt.Dst.Proto, aapkt.Dst.Hardware) - amt[aapkt.Dst.Proto] = amtEntry{ - hwAddr: aapkt.Dst.Hardware, - last: time.Now(), - } + amt.Learn(aapkt.Dst.Proto, aapkt.Dst.Hardware) case aarp.ProbeOp: log.Printf("AARP: %v probing to see if %v is available", aapkt.Src.Hardware, aapkt.Src.Proto) @@ -216,10 +205,7 @@ func main() { ddpkt.Proto, len(ddpkt.Data)) // Glean address info for AMT srcAddr := ddp.Addr{Network: ddpkt.SrcNet, Node: ddpkt.SrcNode} - amt[srcAddr] = amtEntry{ - hwAddr: ethFrame.Src, - last: time.Now(), - } + amt.Learn(srcAddr, ethFrame.Src) log.Printf("DDP: Gleaned that %v -> %v", srcAddr, ethFrame.Src) default: