AMT in status page
This commit is contained in:
parent
15621ec7e0
commit
96ab3f9905
1 changed files with 85 additions and 33 deletions
112
router/aarp.go
112
router/aarp.go
|
@ -39,6 +39,28 @@ const (
|
||||||
aarpRequestTimeout = 10 * time.Second
|
aarpRequestTimeout = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const aarpStatusTemplate = `
|
||||||
|
Status: {{.Status}}<br/>
|
||||||
|
<table>
|
||||||
|
<thead><tr>
|
||||||
|
<th>DDP addr</th>
|
||||||
|
<th>Ethernet addr</th>
|
||||||
|
<th>Last updated</th>
|
||||||
|
<th>Being resolved?</th>
|
||||||
|
</tr></thead>
|
||||||
|
<tbody>
|
||||||
|
{{range $key, $entry := .AMT}}
|
||||||
|
<tr>
|
||||||
|
<td>{{$key.Network}}.{{$key.Node}}</td>
|
||||||
|
<td>{{$entry.HWAddr}}</td>
|
||||||
|
<td>{{$entry.LastUpdated}}</td>
|
||||||
|
<td>{{if $entry.Resolving}}Resolving...{{else}}Resolved{{end}}</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
`
|
||||||
|
|
||||||
// AARPMachine maintains both an Address Mapping Table and handles AARP packets
|
// AARPMachine maintains both an Address Mapping Table and handles AARP packets
|
||||||
// (sending and receiving requests, responses, and probes). This process assumes
|
// (sending and receiving requests, responses, and probes). This process assumes
|
||||||
// a particular network range rather than using the startup range, since this
|
// a particular network range rather than using the startup range, since this
|
||||||
|
@ -53,6 +75,7 @@ type AARPMachine struct {
|
||||||
// probes, so this mutex is not used to enforce a single writer, only
|
// probes, so this mutex is not used to enforce a single writer, only
|
||||||
// consistent reads
|
// consistent reads
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
statusMsg string
|
||||||
myAddr aarp.AddrPair
|
myAddr aarp.AddrPair
|
||||||
probes int
|
probes int
|
||||||
assigned bool
|
assigned bool
|
||||||
|
@ -85,15 +108,26 @@ func (a *AARPMachine) Assigned() <-chan struct{} {
|
||||||
return a.assignedCh
|
return a.assignedCh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AARPMachine) status(ctx context.Context) (any, error) {
|
||||||
|
a.mu.RLock()
|
||||||
|
defer a.mu.RUnlock()
|
||||||
|
return struct {
|
||||||
|
Status string
|
||||||
|
AMT map[ddp.Addr]AMTEntry
|
||||||
|
}{
|
||||||
|
Status: a.statusMsg,
|
||||||
|
AMT: a.addressMappingTable.Dump(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Run executes the machine.
|
// Run executes the machine.
|
||||||
func (a *AARPMachine) Run(ctx context.Context, incomingCh <-chan *ethertalk.Packet) error {
|
func (a *AARPMachine) Run(ctx context.Context, incomingCh <-chan *ethertalk.Packet) error {
|
||||||
ctx, setStatus, done := status.AddSimpleItem(ctx, "AARP")
|
ctx, done := status.AddItem(ctx, "AARP", aarpStatusTemplate, a.status)
|
||||||
defer done()
|
defer done()
|
||||||
|
|
||||||
setStatus("Initialising")
|
|
||||||
|
|
||||||
// Initialise our DDP address with a preferred address (first network.1)
|
// Initialise our DDP address with a preferred address (first network.1)
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
|
a.statusMsg = "Initialising"
|
||||||
a.probes = 0
|
a.probes = 0
|
||||||
a.myAddr.Proto = ddp.Addr{
|
a.myAddr.Proto = ddp.Addr{
|
||||||
Network: ddp.Network(a.cfg.EtherTalk.NetStart),
|
Network: ddp.Network(a.cfg.EtherTalk.NetStart),
|
||||||
|
@ -112,16 +146,16 @@ func (a *AARPMachine) Run(ctx context.Context, incomingCh <-chan *ethertalk.Pack
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
if a.probes >= 10 {
|
if a.probes >= 10 {
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
|
a.statusMsg = fmt.Sprintf("Assigned address %d.%d", a.myAddr.Proto.Network, a.myAddr.Proto.Node)
|
||||||
a.assigned = true
|
a.assigned = true
|
||||||
a.mu.Unlock()
|
a.mu.Unlock()
|
||||||
close(a.assignedCh)
|
close(a.assignedCh)
|
||||||
ticker.Stop()
|
ticker.Stop()
|
||||||
|
|
||||||
setStatus(fmt.Sprintf("Assigned address %d.%d", a.myAddr.Proto.Network, a.myAddr.Proto.Node))
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
|
a.statusMsg = fmt.Sprintf("Probed %d times", a.probes)
|
||||||
a.probes++
|
a.probes++
|
||||||
a.mu.Unlock()
|
a.mu.Unlock()
|
||||||
|
|
||||||
|
@ -129,8 +163,6 @@ func (a *AARPMachine) Run(ctx context.Context, incomingCh <-chan *ethertalk.Pack
|
||||||
log.Printf("Couldn't broadcast a Probe: %v", err)
|
log.Printf("Couldn't broadcast a Probe: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus(fmt.Sprintf("Probed %d times", a.probes))
|
|
||||||
|
|
||||||
case ethFrame, ok := <-incomingCh:
|
case ethFrame, ok := <-incomingCh:
|
||||||
if !ok {
|
if !ok {
|
||||||
incomingCh = nil
|
incomingCh = nil
|
||||||
|
@ -310,18 +342,38 @@ func (a *AARPMachine) request(ddpAddr ddp.Addr) error {
|
||||||
return a.pcapHandle.WritePacketData(reqFrameRaw)
|
return a.pcapHandle.WritePacketData(reqFrameRaw)
|
||||||
}
|
}
|
||||||
|
|
||||||
type amtEntry struct {
|
// AMTEntry is an entry in an address mapping table.
|
||||||
hwAddr ethernet.Addr
|
type AMTEntry struct {
|
||||||
last time.Time
|
// The hardware address that the entry maps to.
|
||||||
|
HWAddr ethernet.Addr
|
||||||
|
|
||||||
|
// The last time this entry was updated.
|
||||||
|
LastUpdated time.Time
|
||||||
|
|
||||||
|
// Whether the address is being resolved.
|
||||||
|
Resolving bool
|
||||||
|
|
||||||
|
// Closed when this entry is updated.
|
||||||
updated chan struct{}
|
updated chan struct{}
|
||||||
requesting bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// addressMappingTable implements a concurrent-safe Address Mapping Table for
|
// addressMappingTable implements a concurrent-safe Address Mapping Table for
|
||||||
// AppleTalk (DDP) addresses to Ethernet hardware addresses.
|
// AppleTalk (DDP) addresses to Ethernet hardware addresses.
|
||||||
type addressMappingTable struct {
|
type addressMappingTable struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
table map[ddp.Addr]*amtEntry
|
table map[ddp.Addr]*AMTEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump returns a copy of the table at a point in time.
|
||||||
|
func (t *addressMappingTable) Dump() map[ddp.Addr]AMTEntry {
|
||||||
|
t.mu.Lock()
|
||||||
|
defer t.mu.Unlock()
|
||||||
|
|
||||||
|
table := make(map[ddp.Addr]AMTEntry, len(t.table))
|
||||||
|
for k, v := range t.table {
|
||||||
|
table[k] = *v
|
||||||
|
}
|
||||||
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
// Learn adds or updates an AMT entry.
|
// Learn adds or updates an AMT entry.
|
||||||
|
@ -329,22 +381,22 @@ func (t *addressMappingTable) Learn(ddpAddr ddp.Addr, hwAddr ethernet.Addr) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
if t.table == nil {
|
if t.table == nil {
|
||||||
t.table = make(map[ddp.Addr]*amtEntry)
|
t.table = make(map[ddp.Addr]*AMTEntry)
|
||||||
}
|
}
|
||||||
oldEnt := t.table[ddpAddr]
|
oldEnt := t.table[ddpAddr]
|
||||||
if oldEnt == nil {
|
if oldEnt == nil {
|
||||||
t.table[ddpAddr] = &amtEntry{
|
t.table[ddpAddr] = &AMTEntry{
|
||||||
hwAddr: hwAddr,
|
HWAddr: hwAddr,
|
||||||
last: time.Now(),
|
LastUpdated: time.Now(),
|
||||||
updated: make(chan struct{}),
|
updated: make(chan struct{}),
|
||||||
requesting: false,
|
Resolving: false,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
oldEnt.hwAddr = hwAddr
|
oldEnt.HWAddr = hwAddr
|
||||||
oldEnt.last = time.Now()
|
oldEnt.LastUpdated = time.Now()
|
||||||
oldEnt.requesting = false
|
oldEnt.Resolving = false
|
||||||
close(oldEnt.updated)
|
close(oldEnt.updated)
|
||||||
oldEnt.updated = make(chan struct{})
|
oldEnt.updated = make(chan struct{})
|
||||||
}
|
}
|
||||||
|
@ -356,25 +408,25 @@ func (t *addressMappingTable) lookupOrWait(ddpAddr ddp.Addr) (ethernet.Addr, <-c
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
if t.table == nil {
|
if t.table == nil {
|
||||||
t.table = make(map[ddp.Addr]*amtEntry)
|
t.table = make(map[ddp.Addr]*AMTEntry)
|
||||||
}
|
}
|
||||||
ent := t.table[ddpAddr]
|
ent := t.table[ddpAddr]
|
||||||
if ent == nil {
|
if ent == nil {
|
||||||
ch := make(chan struct{})
|
ch := make(chan struct{})
|
||||||
t.table[ddpAddr] = &amtEntry{
|
t.table[ddpAddr] = &AMTEntry{
|
||||||
updated: ch,
|
updated: ch,
|
||||||
requesting: true,
|
Resolving: true,
|
||||||
}
|
}
|
||||||
return ethernet.Addr{}, ch, true
|
return ethernet.Addr{}, ch, true
|
||||||
}
|
}
|
||||||
if time.Since(ent.last) >= maxAMTEntryAge {
|
if time.Since(ent.LastUpdated) >= maxAMTEntryAge {
|
||||||
if ent.requesting {
|
if ent.Resolving {
|
||||||
return ent.hwAddr, ent.updated, false
|
return ent.HWAddr, ent.updated, false
|
||||||
}
|
}
|
||||||
ent.requesting = true
|
ent.Resolving = true
|
||||||
return ent.hwAddr, ent.updated, true
|
return ent.HWAddr, ent.updated, true
|
||||||
}
|
}
|
||||||
return ent.hwAddr, nil, false
|
return ent.HWAddr, nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *addressMappingTable) requestingStopped(ddpAddr ddp.Addr) {
|
func (t *addressMappingTable) requestingStopped(ddpAddr ddp.Addr) {
|
||||||
|
@ -387,5 +439,5 @@ func (t *addressMappingTable) requestingStopped(ddpAddr ddp.Addr) {
|
||||||
if ent == nil {
|
if ent == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ent.requesting = false
|
ent.Resolving = false
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue