generalise NBP FwdReq handling
This commit is contained in:
parent
7b496a0519
commit
fbd0a0371f
6 changed files with 101 additions and 76 deletions
|
@ -101,7 +101,7 @@ func Unmarshal(data []byte) (*Packet, error) {
|
|||
t := Tuple{
|
||||
Network: ddp.Network(binary.BigEndian.Uint16(data[:2])),
|
||||
Node: ddp.Node(data[2]),
|
||||
Socket: data[3],
|
||||
Socket: ddp.Socket(data[3]),
|
||||
Enumerator: data[4],
|
||||
}
|
||||
data = data[5:]
|
||||
|
@ -128,7 +128,7 @@ func Unmarshal(data []byte) (*Packet, error) {
|
|||
type Tuple struct {
|
||||
Network ddp.Network
|
||||
Node ddp.Node
|
||||
Socket uint8
|
||||
Socket ddp.Socket
|
||||
Enumerator uint8
|
||||
Object string // length-prefixed
|
||||
Type string // length-prefixed
|
||||
|
@ -147,7 +147,7 @@ func (t *Tuple) writeTo(b *bytes.Buffer) error {
|
|||
}
|
||||
write16(b, t.Network)
|
||||
b.WriteByte(byte(t.Node))
|
||||
b.WriteByte(t.Socket)
|
||||
b.WriteByte(byte(t.Socket))
|
||||
b.WriteByte(t.Enumerator)
|
||||
b.WriteByte(byte(len(t.Object)))
|
||||
b.WriteString(t.Object)
|
||||
|
|
25
main.go
25
main.go
|
@ -358,6 +358,7 @@ func main() {
|
|||
RTMPMachine: rtmpMachine,
|
||||
Router: rooter,
|
||||
}
|
||||
rooter.Ports = append(rooter.Ports, etherTalkPort)
|
||||
routes.InsertEtherTalkDirect(etherTalkPort)
|
||||
for _, az := range etherTalkPort.AvailableZones {
|
||||
zones.Upsert(etherTalkPort.NetStart, az, etherTalkPort)
|
||||
|
@ -468,26 +469,34 @@ func main() {
|
|||
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))
|
||||
// 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))
|
||||
|
||||
// Is it addressed to me?
|
||||
if ddpkt.DstNode == 0 { // Node 0 = any router for the network = me
|
||||
var localPort *router.EtherTalkPort
|
||||
for _, port := range rooter.Ports {
|
||||
if ddpkt.DstNet >= port.NetStart && ddpkt.DstNet <= port.NetEnd {
|
||||
localPort = port
|
||||
break
|
||||
}
|
||||
}
|
||||
if ddpkt.DstNode == 0 && localPort != nil { // Node 0 = any router for the network = me
|
||||
// Is it NBP? FwdReq needs translating.
|
||||
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(ctx, pr, ddpkt); err != nil {
|
||||
// It's NBP, specifically it should be a FwdReq
|
||||
if err := rooter.HandleNBPFromAURP(ctx, ddpkt); err != nil {
|
||||
log.Printf("NBP/DDP/AURP: %v", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Route the packet
|
||||
// Route the packet!
|
||||
if err := rooter.Forward(ctx, ddpkt); err != nil {
|
||||
log.Printf("DDP/AURP: Couldn't route packet: %v", err)
|
||||
}
|
||||
|
|
|
@ -20,12 +20,58 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"slices"
|
||||
|
||||
"gitea.drjosh.dev/josh/jrouter/atalk"
|
||||
"gitea.drjosh.dev/josh/jrouter/atalk/nbp"
|
||||
"github.com/sfiera/multitalk/pkg/ddp"
|
||||
)
|
||||
|
||||
func (rtr *Router) handleNBPFwdReq(ctx context.Context, ddpkt *ddp.ExtPacket, nbpkt *nbp.Packet) error {
|
||||
// A FwdReq was addressed to us. That means a remote router thinks the
|
||||
// zone is available on one or more of our local networks.
|
||||
|
||||
// There must be 1!
|
||||
tuple := &nbpkt.Tuples[0]
|
||||
|
||||
for _, outPort := range rtr.Ports {
|
||||
if !slices.Contains(outPort.AvailableZones, tuple.Zone) {
|
||||
continue
|
||||
}
|
||||
log.Printf("NBP: Converting FwdReq to LkUp (%v)", tuple)
|
||||
|
||||
// Convert it to a LkUp and broadcast on the corresponding port
|
||||
nbpkt.Function = nbp.FunctionLkUp
|
||||
nbpRaw, err := nbpkt.Marshal()
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't marshal LkUp: %v", err)
|
||||
}
|
||||
|
||||
// Inside AppleTalk SE, pp 8-20:
|
||||
// "If the destination network is extended, however, the router must also
|
||||
// change the destination network number to $0000, so that the packet is
|
||||
// received by all nodes on the network (within the correct zone multicast
|
||||
// address)."
|
||||
ddpkt.DstNet = 0x0000
|
||||
ddpkt.DstNode = 0xFF // Broadcast node address within the dest network
|
||||
ddpkt.Data = nbpRaw
|
||||
|
||||
if err := outPort.ZoneMulticast(tuple.Zone, ddpkt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// But also... if it matches us, reply directly with a LkUp-Reply of our own
|
||||
outDDP, err := outPort.helloWorldThisIsMe(nbpkt.NBPID, tuple)
|
||||
if err != nil || outDDP == nil {
|
||||
return err
|
||||
}
|
||||
if err := rtr.Forward(ctx, outDDP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (port *EtherTalkPort) HandleNBP(ctx context.Context, ddpkt *ddp.ExtPacket) error {
|
||||
if ddpkt.Proto != ddp.ProtoNBP {
|
||||
return fmt.Errorf("invalid DDP type %d on socket 2", ddpkt.Proto)
|
||||
|
@ -41,7 +87,7 @@ func (port *EtherTalkPort) HandleNBP(ctx context.Context, ddpkt *ddp.ExtPacket)
|
|||
switch nbpkt.Function {
|
||||
case nbp.FunctionLkUp:
|
||||
// when in AppleTalk, do as Apple Internet Router does...
|
||||
outDDP, err := port.helloWorldThisIsMe(ddpkt, nbpkt.NBPID, &nbpkt.Tuples[0])
|
||||
outDDP, err := port.helloWorldThisIsMe(nbpkt.NBPID, &nbpkt.Tuples[0])
|
||||
if err != nil || outDDP == nil {
|
||||
return err
|
||||
}
|
||||
|
@ -50,7 +96,7 @@ func (port *EtherTalkPort) HandleNBP(ctx context.Context, ddpkt *ddp.ExtPacket)
|
|||
return port.Send(ctx, outDDP)
|
||||
|
||||
case nbp.FunctionFwdReq:
|
||||
// TODO: handle FwdReq input
|
||||
return port.Router.handleNBPFwdReq(ctx, ddpkt, nbpkt)
|
||||
|
||||
case nbp.FunctionBrRq:
|
||||
return port.handleNBPBrRq(ctx, ddpkt, nbpkt)
|
||||
|
@ -58,16 +104,20 @@ func (port *EtherTalkPort) HandleNBP(ctx context.Context, ddpkt *ddp.ExtPacket)
|
|||
default:
|
||||
return fmt.Errorf("TODO: handle function %v", nbpkt.Function)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (port *EtherTalkPort) handleNBPBrRq(ctx context.Context, ddpkt *ddp.ExtPacket, nbpkt *nbp.Packet) error {
|
||||
// A BrRq was addressed to us. The sender (on a local network) is aware that
|
||||
// the network is extended and routed, and instead of broadcasting a LkUp
|
||||
// itself, is asking us to do it.
|
||||
|
||||
// There must be 1!
|
||||
tuple := &nbpkt.Tuples[0]
|
||||
|
||||
if tuple.Zone == "" || tuple.Zone == "*" {
|
||||
tuple.Zone = port.DefaultZoneName
|
||||
}
|
||||
// This logic would be required on a non-extended network:
|
||||
// if tuple.Zone == "" || tuple.Zone == "*" {
|
||||
// tuple.Zone = port.DefaultZoneName
|
||||
// }
|
||||
|
||||
zones := port.Router.ZoneTable.LookupName(tuple.Zone)
|
||||
|
||||
|
@ -109,7 +159,7 @@ func (port *EtherTalkPort) handleNBPBrRq(ctx context.Context, ddpkt *ddp.ExtPack
|
|||
|
||||
// But also...if we match the query, reply as though it was a LkUp
|
||||
// This uses the *input* port information.
|
||||
outDDP2, err := port.helloWorldThisIsMe(ddpkt, nbpkt.NBPID, tuple)
|
||||
outDDP2, err := port.helloWorldThisIsMe(nbpkt.NBPID, tuple)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -117,8 +167,8 @@ func (port *EtherTalkPort) handleNBPBrRq(ctx context.Context, ddpkt *ddp.ExtPack
|
|||
continue
|
||||
}
|
||||
log.Print("NBP: Replying to BrRq directly with LkUp-Reply for myself")
|
||||
// Can reply to BrRq on the same port we got it, because it wasn't
|
||||
// routed
|
||||
// Can reply to this BrRq on the same port we got it, because it
|
||||
// wasn't routed
|
||||
if err := port.Send(ctx, outDDP2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -158,7 +208,7 @@ func (port *EtherTalkPort) handleNBPBrRq(ctx context.Context, ddpkt *ddp.ExtPack
|
|||
}
|
||||
|
||||
// Returns an NBP LkUp-Reply for the router itself, with the address from this port.
|
||||
func (port *EtherTalkPort) helloWorldThisIsMe(ddpkt *ddp.ExtPacket, nbpID uint8, tuple *nbp.Tuple) (*ddp.ExtPacket, error) {
|
||||
func (port *EtherTalkPort) helloWorldThisIsMe(nbpID uint8, tuple *nbp.Tuple) (*ddp.ExtPacket, error) {
|
||||
if tuple.Object != "jrouter" && tuple.Object != "=" {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -187,13 +237,23 @@ func (port *EtherTalkPort) helloWorldThisIsMe(ddpkt *ddp.ExtPacket, nbpID uint8,
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't marshal LkUp-Reply: %v", err)
|
||||
}
|
||||
|
||||
// Inside AppleTalk SE, pp 7-16:
|
||||
// "In BrRq, FwdReq, and LkUp packets, which carry only a single tuple, the
|
||||
// address field contains the internet address of the requester, allowing
|
||||
// the responder to address the LkUp-Reply datagram."
|
||||
// Inside AppleTalk SE, pp 8-20:
|
||||
// "Note: NBP is defined so that the router's NBP process does not
|
||||
// participate in the NBP response process; the response is sent directly to
|
||||
// the original requester through DDP. It is important that the original
|
||||
// requester's field be obtained from the address field of the NBP tuple."
|
||||
return &ddp.ExtPacket{
|
||||
ExtHeader: ddp.ExtHeader{
|
||||
Size: uint16(len(respRaw)) + atalk.DDPExtHeaderSize,
|
||||
Cksum: 0,
|
||||
DstNet: ddpkt.SrcNet,
|
||||
DstNode: ddpkt.SrcNode,
|
||||
DstSocket: ddpkt.SrcSocket,
|
||||
DstNet: tuple.Network,
|
||||
DstNode: tuple.Node,
|
||||
DstSocket: tuple.Socket,
|
||||
SrcNet: port.MyAddr.Network,
|
||||
SrcNode: port.MyAddr.Node,
|
||||
SrcSocket: 2,
|
||||
|
|
|
@ -19,13 +19,12 @@ package router
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"gitea.drjosh.dev/josh/jrouter/atalk/nbp"
|
||||
"github.com/sfiera/multitalk/pkg/ddp"
|
||||
)
|
||||
|
||||
func (rtr *Router) HandleNBPInAURP(ctx context.Context, peer *AURPPeer, ddpkt *ddp.ExtPacket) error {
|
||||
func (rtr *Router) HandleNBPFromAURP(ctx context.Context, ddpkt *ddp.ExtPacket) error {
|
||||
if ddpkt.Proto != ddp.ProtoNBP {
|
||||
return fmt.Errorf("invalid DDP type %d on socket 2", ddpkt.Proto)
|
||||
}
|
||||
|
@ -37,50 +36,5 @@ func (rtr *Router) HandleNBPInAURP(ctx context.Context, peer *AURPPeer, ddpkt *d
|
|||
// It's something else??
|
||||
return fmt.Errorf("can't handle %v", nbpkt.Function)
|
||||
}
|
||||
|
||||
if len(nbpkt.Tuples) < 1 {
|
||||
return fmt.Errorf("no tuples in NBP packet")
|
||||
}
|
||||
tuple := &nbpkt.Tuples[0]
|
||||
|
||||
zones := rtr.ZoneTable.LookupName(tuple.Zone)
|
||||
for _, z := range zones {
|
||||
if z.LocalPort == nil {
|
||||
continue
|
||||
}
|
||||
port := z.LocalPort
|
||||
|
||||
log.Printf("NBP/DDP/AURP: Converting FwdReq to LkUp (%v)", tuple)
|
||||
|
||||
// Convert it to a LkUp and broadcast on EtherTalk
|
||||
nbpkt.Function = nbp.FunctionLkUp
|
||||
nbpRaw, err := nbpkt.Marshal()
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't marshal LkUp: %v", err)
|
||||
}
|
||||
|
||||
// "If the destination network is extended, however, the router must also
|
||||
// change the destination network number to $0000, so that the packet is
|
||||
// received by all nodes on the network (within the correct zone multicast
|
||||
// address)."
|
||||
ddpkt.DstNet = 0x0000
|
||||
ddpkt.DstNode = 0xFF // Broadcast node address within the dest network
|
||||
ddpkt.Data = nbpRaw
|
||||
|
||||
if err := port.ZoneMulticast(tuple.Zone, ddpkt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// But also... if it matches us, reply directly with a LkUp-Reply of our own
|
||||
outDDP, err := port.helloWorldThisIsMe(ddpkt, nbpkt.NBPID, tuple)
|
||||
if err != nil || outDDP == nil {
|
||||
return err
|
||||
}
|
||||
log.Print("NBP/DDP/AURP: Replying to BrRq with LkUp-Reply for myself")
|
||||
if err := rtr.Forward(ctx, outDDP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return rtr.handleNBPFwdReq(ctx, ddpkt, nbpkt)
|
||||
}
|
||||
|
|
|
@ -84,10 +84,10 @@ func (port *EtherTalkPort) Serve(ctx context.Context) {
|
|||
log.Printf("Couldn't unmarshal DDP packet: %v", err)
|
||||
continue
|
||||
}
|
||||
log.Printf("DDP: src (%d.%d s %d) dst (%d.%d s %d) proto %d data len %d",
|
||||
ddpkt.SrcNet, ddpkt.SrcNode, ddpkt.SrcSocket,
|
||||
ddpkt.DstNet, ddpkt.DstNode, ddpkt.DstSocket,
|
||||
ddpkt.Proto, len(ddpkt.Data))
|
||||
// log.Printf("DDP: src (%d.%d s %d) dst (%d.%d s %d) proto %d data len %d",
|
||||
// ddpkt.SrcNet, ddpkt.SrcNode, ddpkt.SrcSocket,
|
||||
// ddpkt.DstNet, ddpkt.DstNode, ddpkt.DstSocket,
|
||||
// ddpkt.Proto, len(ddpkt.Data))
|
||||
|
||||
// Glean address info for AMT, but only if SrcNet is our net
|
||||
// (If it's not our net, then it was routed from elsewhere, and
|
||||
|
@ -103,6 +103,7 @@ func (port *EtherTalkPort) Serve(ctx context.Context) {
|
|||
if !ok {
|
||||
continue
|
||||
}
|
||||
port.MyAddr = myAddr.Proto
|
||||
|
||||
// Our network?
|
||||
// "The network number 0 is reserved to mean unknown; by default
|
||||
|
|
|
@ -27,6 +27,7 @@ type Router struct {
|
|||
Config *Config
|
||||
RouteTable *RouteTable
|
||||
ZoneTable *ZoneTable
|
||||
Ports []*EtherTalkPort
|
||||
}
|
||||
|
||||
// Forward routes a packet towards the right destination.
|
||||
|
|
Loading…
Reference in a new issue