Fix NBP replies for the router itself

This commit is contained in:
Josh Deprez 2024-04-21 17:47:58 +10:00
parent 5103850d6e
commit 3953511438
Signed by: josh
SSH key fingerprint: SHA256:zZji7w1Ilh2RuUpbQcqkLPrqmRwpiCSycbF2EfKm6Kw
4 changed files with 146 additions and 97 deletions

90
main.go
View file

@ -266,9 +266,6 @@ func main() {
continue
}
// TODO: filter ethFrame.Dst to myHWAddr, the broadcast address,
// or the relevant zone multicast address
switch ethFrame.SNAPProto {
case ethertalk.AARPProto:
// log.Print("Got an AARP frame")
@ -385,7 +382,54 @@ func main() {
return
}
if apkt, ok := pkt.(*aurp.AppleTalkPacket); ok {
log.Printf("AURP: Got %T from %v", pkt, 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
}
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
}
// Route or otherwise handle the encapsulated AppleTalk traffic
ddpkt := new(ddp.ExtPacket)
if err := ddp.ExtUnmarshal(apkt.Data, ddpkt); err != nil {
log.Printf("AURP: Couldn't unmarshal encapsulated DDP packet: %v", err)
@ -424,7 +468,7 @@ func main() {
continue
}
// It's NBP
if err := rooter.HandleNBPInAURP(ddpkt); err != nil {
if err := rooter.HandleNBPInAURP(pr, ddpkt); err != nil {
log.Printf("NBP/DDP/AURP: %v", err)
}
continue
@ -453,41 +497,9 @@ func main() {
log.Printf("DDP/AURP: couldn't write output frame to device: %v", err)
}
continue
}
log.Printf("AURP: Got %T from %v", pkt, 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)
}
// 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
default:
log.Printf("AURP: Got unknown packet type %v", dh.PacketType)
}
}
}

View file

@ -42,62 +42,16 @@ func (rtr *Router) HandleNBP(srcHWAddr ethernet.Addr, ddpkt *ddp.ExtPacket) erro
switch nbpkt.Function {
case nbp.FunctionLkUp:
// when in AppleTalk, do as Apple Internet Router does...
tuple := nbpkt.Tuples[0]
if tuple.Object != "jrouter" && tuple.Object != "=" {
return nil
}
if tuple.Type != "AppleRouter" && tuple.Type != "=" {
return nil
}
if tuple.Zone != rtr.Config.EtherTalk.ZoneName && tuple.Zone != "*" && tuple.Zone != "" {
return nil
}
respPkt := &nbp.Packet{
Function: nbp.FunctionLkUpReply,
NBPID: nbpkt.NBPID,
Tuples: []nbp.Tuple{
{
Network: rtr.MyDDPAddr.Network,
Node: rtr.MyDDPAddr.Node,
Socket: 253,
Enumerator: 0,
Object: "jrouter",
Type: "AppleRouter",
Zone: rtr.Config.EtherTalk.ZoneName,
},
},
}
respRaw, err := respPkt.Marshal()
if err != nil {
return fmt.Errorf("couldn't marshal LkUp-Reply: %v", err)
}
outDDP := ddp.ExtPacket{
ExtHeader: ddp.ExtHeader{
Size: uint16(len(respRaw)) + atalk.DDPExtHeaderSize,
Cksum: 0,
DstNet: ddpkt.SrcNet,
DstNode: ddpkt.SrcNode,
DstSocket: ddpkt.SrcSocket,
SrcNet: rtr.MyDDPAddr.Network,
SrcNode: rtr.MyDDPAddr.Node,
SrcSocket: 2,
},
Data: respRaw,
}
outFrame, err := ethertalk.AppleTalk(rtr.MyHWAddr, outDDP)
if err != nil {
outDDP, err := rtr.helloWorldThisIsMe(ddpkt, nbpkt.NBPID, &nbpkt.Tuples[0])
if err != nil || outDDP == nil {
return err
}
outFrame.Dst = srcHWAddr
outFrameRaw, err := ethertalk.Marshal(*outFrame)
if err != nil {
return err
}
return rtr.PcapHandle.WritePacketData(outFrameRaw)
return rtr.sendEtherTalkDDP(srcHWAddr, outDDP)
case nbp.FunctionBrRq:
// There must be 1!
tuple := &nbpkt.Tuples[0]
ethDst := ethertalk.AppleTalkBroadcast
if tuple.Zone != "*" && tuple.Zone != "" {
ethDst = atalk.MulticastAddr(tuple.Zone)
@ -138,6 +92,18 @@ func (rtr *Router) HandleNBP(srcHWAddr ethernet.Addr, ddpkt *ddp.ExtPacket) erro
return err
}
// But also...if we match the query, reply as though it was a LkUp
outDDP2, err := rtr.helloWorldThisIsMe(ddpkt, nbpkt.NBPID, tuple)
if err != nil {
return err
}
if outDDP2 == nil {
continue
}
if err := rtr.sendEtherTalkDDP(srcHWAddr, outDDP2); err != nil {
return err
}
continue
}
@ -180,3 +146,47 @@ func (rtr *Router) HandleNBP(srcHWAddr ethernet.Addr, ddpkt *ddp.ExtPacket) erro
return nil
}
func (rtr *Router) helloWorldThisIsMe(ddpkt *ddp.ExtPacket, nbpID uint8, tuple *nbp.Tuple) (*ddp.ExtPacket, error) {
if tuple.Object != "jrouter" && tuple.Object != "=" {
return nil, nil
}
if tuple.Type != "AppleRouter" && tuple.Type != "=" {
return nil, nil
}
if tuple.Zone != rtr.Config.EtherTalk.ZoneName && tuple.Zone != "*" && tuple.Zone != "" {
return nil, nil
}
respPkt := &nbp.Packet{
Function: nbp.FunctionLkUpReply,
NBPID: nbpID,
Tuples: []nbp.Tuple{
{
Network: rtr.MyDDPAddr.Network,
Node: rtr.MyDDPAddr.Node,
Socket: 253,
Enumerator: 0,
Object: "jrouter",
Type: "AppleRouter",
Zone: rtr.Config.EtherTalk.ZoneName,
},
},
}
respRaw, err := respPkt.Marshal()
if err != nil {
return nil, fmt.Errorf("couldn't marshal LkUp-Reply: %v", err)
}
return &ddp.ExtPacket{
ExtHeader: ddp.ExtHeader{
Size: uint16(len(respRaw)) + atalk.DDPExtHeaderSize,
Cksum: 0,
DstNet: ddpkt.SrcNet,
DstNode: ddpkt.SrcNode,
DstSocket: ddpkt.SrcSocket,
SrcNet: rtr.MyDDPAddr.Network,
SrcNode: rtr.MyDDPAddr.Node,
SrcSocket: 2,
},
Data: respRaw,
}, nil
}

View file

@ -26,7 +26,7 @@ import (
"github.com/sfiera/multitalk/pkg/ethertalk"
)
func (rtr *Router) HandleNBPInAURP(ddpkt *ddp.ExtPacket) error {
func (rtr *Router) HandleNBPInAURP(peer *Peer, ddpkt *ddp.ExtPacket) error {
if ddpkt.Proto != ddp.ProtoNBP {
return fmt.Errorf("invalid DDP type %d on socket 2", ddpkt.Proto)
}
@ -44,6 +44,12 @@ func (rtr *Router) HandleNBPInAURP(ddpkt *ddp.ExtPacket) error {
}
tuple := &nbpkt.Tuples[0]
if tuple.Zone != rtr.Config.EtherTalk.ZoneName {
return fmt.Errorf("FwdReq querying zone %q, which is not our zone", tuple.Zone)
}
// TODO: Route the FwdReq to another router if it's not our zone
log.Printf("NBP/DDP/AURP: Converting FwdReq to LkUp (%v)", tuple)
// Convert it to a LkUp and broadcast on EtherTalk
@ -61,16 +67,23 @@ func (rtr *Router) HandleNBPInAURP(ddpkt *ddp.ExtPacket) error {
ddpkt.DstNode = 0xFF // Broadcast node address within the dest network
ddpkt.Data = nbpRaw
outFrame, err := ethertalk.AppleTalk(rtr.MyHWAddr, *ddpkt)
if err != nil {
return err
}
dstEth := ethertalk.AppleTalkBroadcast
if tuple.Zone != "*" && tuple.Zone != "" {
outFrame.Dst = atalk.MulticastAddr(tuple.Zone)
dstEth = atalk.MulticastAddr(tuple.Zone)
}
outFrameRaw, err := ethertalk.Marshal(*outFrame)
if err := rtr.sendEtherTalkDDP(dstEth, ddpkt); err != nil {
return err
}
// But also... if it matches us, reply directly with a LkUp-Reply of our own
outDDP, err := rtr.helloWorldThisIsMe(ddpkt, nbpkt.NBPID, tuple)
if err != nil || outDDP == nil {
return err
}
outDDPRaw, err := ddp.ExtMarshal(*outDDP)
if err != nil {
return err
}
return rtr.PcapHandle.WritePacketData(outFrameRaw)
_, err = peer.Send(peer.Transport.NewAppleTalkPacket(outDDPRaw))
return err
}

View file

@ -20,6 +20,7 @@ import (
"github.com/google/gopacket/pcap"
"github.com/sfiera/multitalk/pkg/ddp"
"github.com/sfiera/multitalk/pkg/ethernet"
"github.com/sfiera/multitalk/pkg/ethertalk"
)
type Router struct {
@ -30,3 +31,16 @@ type Router struct {
RouteTable *RoutingTable
ZoneTable *ZoneTable
}
func (rtr *Router) sendEtherTalkDDP(dstEth ethernet.Addr, pkt *ddp.ExtPacket) error {
outFrame, err := ethertalk.AppleTalk(rtr.MyHWAddr, *pkt)
if err != nil {
return err
}
outFrame.Dst = dstEth
outFrameRaw, err := ethertalk.Marshal(*outFrame)
if err != nil {
return err
}
return rtr.PcapHandle.WritePacketData(outFrameRaw)
}