This commit is contained in:
Josh Deprez 2024-04-23 11:56:15 +10:00
parent 1b33388e2a
commit 74936edbdd
Signed by: josh
SSH key fingerprint: SHA256:zZji7w1Ilh2RuUpbQcqkLPrqmRwpiCSycbF2EfKm6Kw

243
main.go
View file

@ -127,15 +127,16 @@ func main() {
pcapHandle.Close() pcapHandle.Close()
log.Fatalf("Couldn't set BPF filter on packet capture: %v", err) log.Fatalf("Couldn't set BPF filter on packet capture: %v", err)
} }
defer pcapHandle.Close() // defer pcapHandle.Close()
// Wait until all peer handlers have finished before closing the port // Wait until all peer handlers have finished before closing the port
var handlersWG sync.WaitGroup var handlersWG sync.WaitGroup
defer func() { defer handlersWG.Wait()
log.Print("Waiting for handlers to return...") // defer func() {
handlersWG.Wait() // // log.Print("Waiting for handlers to return...")
ln.Close() // // handlersWG.Wait()
}() // // ln.Close()
// }()
goPeerHandler := func(p *router.Peer) { goPeerHandler := func(p *router.Peer) {
handlersWG.Add(1) handlersWG.Add(1)
go func() { go func() {
@ -244,9 +245,9 @@ func main() {
} }
rawPkt, _, err := pcapHandle.ReadPacketData() rawPkt, _, err := pcapHandle.ReadPacketData()
if errors.Is(err, pcap.NextErrorTimeoutExpired) { // if errors.Is(err, pcap.NextErrorTimeoutExpired) {
continue // continue
} // }
if errors.Is(err, io.EOF) || errors.Is(err, pcap.NextErrorNoMorePackets) { if errors.Is(err, io.EOF) || errors.Is(err, pcap.NextErrorNoMorePackets) {
return return
} }
@ -357,132 +358,134 @@ func main() {
}() }()
// ----------------------------- AURP inbound ----------------------------- // ----------------------------- AURP inbound -----------------------------
for { go func() {
if ctx.Err() != nil { for {
return if ctx.Err() != nil {
}
ln.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
pktbuf := make([]byte, 4096)
pktlen, raddr, readErr := ln.ReadFromUDP(pktbuf)
var operr *net.OpError
if errors.As(readErr, &operr) && operr.Timeout() {
continue
}
// log.Printf("AURP: Received packet of length %d from %v", pktlen, raddr)
dh, pkt, parseErr := aurp.ParsePacket(pktbuf[:pktlen])
if parseErr != nil {
log.Printf("AURP: Failed to parse packet: %v", parseErr)
continue
}
if readErr != nil {
log.Printf("AURP: Failed to read packet: %v", readErr)
return
}
log.Printf("AURP: Got %T from %v (%v)", pkt, raddr, dh.SourceDI)
// Existing peer?
ra := udpAddrFromNet(raddr)
pr := peers[ra]
if pr == nil {
// New peer!
pr = &router.Peer{
Config: cfg,
Transport: &aurp.Transport{
LocalDI: localDI,
RemoteDI: dh.SourceDI, // platinum rule
LocalConnID: nextConnID,
},
UDPConn: ln,
RemoteAddr: raddr,
RecieveCh: make(chan aurp.Packet, 1024),
RoutingTable: routes,
ZoneTable: zones,
Reconnect: false,
}
aurp.Inc(&nextConnID)
peers[ra] = pr
goPeerHandler(pr)
}
switch dh.PacketType {
case aurp.PacketTypeRouting:
// It's AURP routing data.
// Pass the packet to the goroutine in charge of this peer.
select {
case pr.RecieveCh <- pkt:
// That's it for us.
case <-ctx.Done():
return return
} }
continue // ln.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
pktbuf := make([]byte, 4096)
pktlen, raddr, readErr := ln.ReadFromUDP(pktbuf)
case aurp.PacketTypeAppleTalk: // var operr *net.OpError
apkt, ok := pkt.(*aurp.AppleTalkPacket) // if errors.As(readErr, &operr) && operr.Timeout() {
if !ok { // continue
log.Printf("AURP: Got %T but domain header packet type was %v ?", pkt, dh.PacketType) // }
// log.Printf("AURP: Received packet of length %d from %v", pktlen, raddr)
dh, pkt, parseErr := aurp.ParsePacket(pktbuf[:pktlen])
if parseErr != nil {
log.Printf("AURP: Failed to parse packet: %v", parseErr)
continue continue
} }
if readErr != nil {
// Route or otherwise handle the encapsulated AppleTalk traffic log.Printf("AURP: Failed to read packet: %v", readErr)
ddpkt := new(ddp.ExtPacket) return
if err := ddp.ExtUnmarshal(apkt.Data, ddpkt); err != nil {
log.Printf("AURP: Couldn't unmarshal encapsulated DDP packet: %v", err)
continue
}
log.Printf("DDP/AURP: Got %d.%d.%d -> %d.%d.%d proto %d data len %d",
ddpkt.SrcNet, ddpkt.SrcNode, ddpkt.SrcSocket,
ddpkt.DstNet, ddpkt.DstNode, ddpkt.DstSocket,
ddpkt.Proto, len(ddpkt.Data))
// "Route" the packet
// Since for now there's only one local network, the routing
// decision is pretty easy
// TODO: Fix this to support other AppleTalk routers
if ddpkt.DstNet < cfg.EtherTalk.NetStart || ddpkt.DstNet > cfg.EtherTalk.NetEnd {
log.Print("DDP/AURP: dropping packet not addressed to our EtherTalk range")
continue
} }
// Check and adjust the Hop Count log.Printf("AURP: Got %T from %v (%v)", pkt, raddr, dh.SourceDI)
// Note the ddp package doesn't make this simple
hopCount := (ddpkt.Size & 0x3C00) >> 10
if hopCount >= 15 {
log.Printf("DDP/AURP: hop count exceeded (%d >= 15)", hopCount)
continue
}
hopCount++
ddpkt.Size &^= 0x3C00
ddpkt.Size |= hopCount << 10
// Is it addressed to me? Is it NBP? // Existing peer?
if ddpkt.DstNode == 0 { // Node 0 = the router for the network ra := udpAddrFromNet(raddr)
if ddpkt.DstSocket != 2 { pr := peers[ra]
// Something else?? TODO if pr == nil {
log.Printf("DDP/AURP: I don't have anything 'listening' on socket %d", ddpkt.DstSocket) // New peer!
pr = &router.Peer{
Config: cfg,
Transport: &aurp.Transport{
LocalDI: localDI,
RemoteDI: dh.SourceDI, // platinum rule
LocalConnID: nextConnID,
},
UDPConn: ln,
RemoteAddr: raddr,
RecieveCh: make(chan aurp.Packet, 1024),
RoutingTable: routes,
ZoneTable: zones,
Reconnect: false,
}
aurp.Inc(&nextConnID)
peers[ra] = pr
goPeerHandler(pr)
}
switch dh.PacketType {
case aurp.PacketTypeRouting:
// It's AURP routing data.
// Pass the packet to the goroutine in charge of this peer.
select {
case pr.RecieveCh <- pkt:
// That's it for us.
case <-ctx.Done():
return
}
continue
case aurp.PacketTypeAppleTalk:
apkt, ok := pkt.(*aurp.AppleTalkPacket)
if !ok {
log.Printf("AURP: Got %T but domain header packet type was %v ?", pkt, dh.PacketType)
continue continue
} }
// It's NBP
if err := rooter.HandleNBPInAURP(pr, ddpkt); err != nil { // Route or otherwise handle the encapsulated AppleTalk traffic
log.Printf("NBP/DDP/AURP: %v", err) ddpkt := new(ddp.ExtPacket)
if err := ddp.ExtUnmarshal(apkt.Data, ddpkt); err != nil {
log.Printf("AURP: Couldn't unmarshal encapsulated DDP packet: %v", err)
continue
}
log.Printf("DDP/AURP: Got %d.%d.%d -> %d.%d.%d proto %d data len %d",
ddpkt.SrcNet, ddpkt.SrcNode, ddpkt.SrcSocket,
ddpkt.DstNet, ddpkt.DstNode, ddpkt.DstSocket,
ddpkt.Proto, len(ddpkt.Data))
// "Route" the packet
// Since for now there's only one local network, the routing
// decision is pretty easy
// TODO: Fix this to support other AppleTalk routers
if ddpkt.DstNet < cfg.EtherTalk.NetStart || ddpkt.DstNet > cfg.EtherTalk.NetEnd {
log.Print("DDP/AURP: dropping packet not addressed to our EtherTalk range")
continue
}
// Check and adjust the Hop Count
// Note the ddp package doesn't make this simple
hopCount := (ddpkt.Size & 0x3C00) >> 10
if hopCount >= 15 {
log.Printf("DDP/AURP: hop count exceeded (%d >= 15)", hopCount)
continue
}
hopCount++
ddpkt.Size &^= 0x3C00
ddpkt.Size |= hopCount << 10
// Is it addressed to me? Is it NBP?
if ddpkt.DstNode == 0 { // Node 0 = the router for the network
if ddpkt.DstSocket != 2 {
// Something else?? TODO
log.Printf("DDP/AURP: I don't have anything 'listening' on socket %d", ddpkt.DstSocket)
continue
}
// It's NBP
if err := rooter.HandleNBPInAURP(pr, ddpkt); err != nil {
log.Printf("NBP/DDP/AURP: %v", err)
}
continue
}
// Note: resolving AARP can block
if err := rooter.SendEtherTalkDDP(ctx, ddpkt); err != nil {
log.Printf("DDP/AURP: couldn't send Ethertalk out: %v", err)
} }
continue continue
}
// Note: resolving AARP can block default:
if err := rooter.SendEtherTalkDDP(ctx, ddpkt); err != nil { log.Printf("AURP: Got unknown packet type %v", dh.PacketType)
log.Printf("DDP/AURP: couldn't send Ethertalk out: %v", err)
} }
continue
default:
log.Printf("AURP: Got unknown packet type %v", dh.PacketType)
} }
} }()
} }
// Hashable net.UDPAddr // Hashable net.UDPAddr