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{
|
t := Tuple{
|
||||||
Network: ddp.Network(binary.BigEndian.Uint16(data[:2])),
|
Network: ddp.Network(binary.BigEndian.Uint16(data[:2])),
|
||||||
Node: ddp.Node(data[2]),
|
Node: ddp.Node(data[2]),
|
||||||
Socket: data[3],
|
Socket: ddp.Socket(data[3]),
|
||||||
Enumerator: data[4],
|
Enumerator: data[4],
|
||||||
}
|
}
|
||||||
data = data[5:]
|
data = data[5:]
|
||||||
|
@ -128,7 +128,7 @@ func Unmarshal(data []byte) (*Packet, error) {
|
||||||
type Tuple struct {
|
type Tuple struct {
|
||||||
Network ddp.Network
|
Network ddp.Network
|
||||||
Node ddp.Node
|
Node ddp.Node
|
||||||
Socket uint8
|
Socket ddp.Socket
|
||||||
Enumerator uint8
|
Enumerator uint8
|
||||||
Object string // length-prefixed
|
Object string // length-prefixed
|
||||||
Type string // length-prefixed
|
Type string // length-prefixed
|
||||||
|
@ -147,7 +147,7 @@ func (t *Tuple) writeTo(b *bytes.Buffer) error {
|
||||||
}
|
}
|
||||||
write16(b, t.Network)
|
write16(b, t.Network)
|
||||||
b.WriteByte(byte(t.Node))
|
b.WriteByte(byte(t.Node))
|
||||||
b.WriteByte(t.Socket)
|
b.WriteByte(byte(t.Socket))
|
||||||
b.WriteByte(t.Enumerator)
|
b.WriteByte(t.Enumerator)
|
||||||
b.WriteByte(byte(len(t.Object)))
|
b.WriteByte(byte(len(t.Object)))
|
||||||
b.WriteString(t.Object)
|
b.WriteString(t.Object)
|
||||||
|
|
25
main.go
25
main.go
|
@ -358,6 +358,7 @@ func main() {
|
||||||
RTMPMachine: rtmpMachine,
|
RTMPMachine: rtmpMachine,
|
||||||
Router: rooter,
|
Router: rooter,
|
||||||
}
|
}
|
||||||
|
rooter.Ports = append(rooter.Ports, etherTalkPort)
|
||||||
routes.InsertEtherTalkDirect(etherTalkPort)
|
routes.InsertEtherTalkDirect(etherTalkPort)
|
||||||
for _, az := range etherTalkPort.AvailableZones {
|
for _, az := range etherTalkPort.AvailableZones {
|
||||||
zones.Upsert(etherTalkPort.NetStart, az, etherTalkPort)
|
zones.Upsert(etherTalkPort.NetStart, az, etherTalkPort)
|
||||||
|
@ -468,26 +469,34 @@ func main() {
|
||||||
log.Printf("AURP: Couldn't unmarshal encapsulated DDP packet: %v", err)
|
log.Printf("AURP: Couldn't unmarshal encapsulated DDP packet: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Printf("DDP/AURP: Got %d.%d.%d -> %d.%d.%d proto %d data len %d",
|
// log.Printf("DDP/AURP: Got %d.%d.%d -> %d.%d.%d proto %d data len %d",
|
||||||
ddpkt.SrcNet, ddpkt.SrcNode, ddpkt.SrcSocket,
|
// ddpkt.SrcNet, ddpkt.SrcNode, ddpkt.SrcSocket,
|
||||||
ddpkt.DstNet, ddpkt.DstNode, ddpkt.DstSocket,
|
// ddpkt.DstNet, ddpkt.DstNode, ddpkt.DstSocket,
|
||||||
ddpkt.Proto, len(ddpkt.Data))
|
// ddpkt.Proto, len(ddpkt.Data))
|
||||||
|
|
||||||
// Is it addressed to me?
|
// 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.
|
// Is it NBP? FwdReq needs translating.
|
||||||
if ddpkt.DstSocket != 2 {
|
if ddpkt.DstSocket != 2 {
|
||||||
// Something else?? TODO
|
// Something else?? TODO
|
||||||
log.Printf("DDP/AURP: I don't have anything 'listening' on socket %d", ddpkt.DstSocket)
|
log.Printf("DDP/AURP: I don't have anything 'listening' on socket %d", ddpkt.DstSocket)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// It's NBP
|
// It's NBP, specifically it should be a FwdReq
|
||||||
if err := rooter.HandleNBPInAURP(ctx, pr, ddpkt); err != nil {
|
if err := rooter.HandleNBPFromAURP(ctx, ddpkt); err != nil {
|
||||||
log.Printf("NBP/DDP/AURP: %v", err)
|
log.Printf("NBP/DDP/AURP: %v", err)
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route the packet
|
// Route the packet!
|
||||||
if err := rooter.Forward(ctx, ddpkt); err != nil {
|
if err := rooter.Forward(ctx, ddpkt); err != nil {
|
||||||
log.Printf("DDP/AURP: Couldn't route packet: %v", err)
|
log.Printf("DDP/AURP: Couldn't route packet: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,58 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"gitea.drjosh.dev/josh/jrouter/atalk"
|
"gitea.drjosh.dev/josh/jrouter/atalk"
|
||||||
"gitea.drjosh.dev/josh/jrouter/atalk/nbp"
|
"gitea.drjosh.dev/josh/jrouter/atalk/nbp"
|
||||||
"github.com/sfiera/multitalk/pkg/ddp"
|
"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 {
|
func (port *EtherTalkPort) HandleNBP(ctx context.Context, ddpkt *ddp.ExtPacket) error {
|
||||||
if ddpkt.Proto != ddp.ProtoNBP {
|
if ddpkt.Proto != ddp.ProtoNBP {
|
||||||
return fmt.Errorf("invalid DDP type %d on socket 2", ddpkt.Proto)
|
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 {
|
switch nbpkt.Function {
|
||||||
case nbp.FunctionLkUp:
|
case nbp.FunctionLkUp:
|
||||||
// when in AppleTalk, do as Apple Internet Router does...
|
// 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 {
|
if err != nil || outDDP == nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -50,7 +96,7 @@ func (port *EtherTalkPort) HandleNBP(ctx context.Context, ddpkt *ddp.ExtPacket)
|
||||||
return port.Send(ctx, outDDP)
|
return port.Send(ctx, outDDP)
|
||||||
|
|
||||||
case nbp.FunctionFwdReq:
|
case nbp.FunctionFwdReq:
|
||||||
// TODO: handle FwdReq input
|
return port.Router.handleNBPFwdReq(ctx, ddpkt, nbpkt)
|
||||||
|
|
||||||
case nbp.FunctionBrRq:
|
case nbp.FunctionBrRq:
|
||||||
return port.handleNBPBrRq(ctx, ddpkt, nbpkt)
|
return port.handleNBPBrRq(ctx, ddpkt, nbpkt)
|
||||||
|
@ -58,16 +104,20 @@ func (port *EtherTalkPort) HandleNBP(ctx context.Context, ddpkt *ddp.ExtPacket)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("TODO: handle function %v", nbpkt.Function)
|
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 {
|
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!
|
// There must be 1!
|
||||||
tuple := &nbpkt.Tuples[0]
|
tuple := &nbpkt.Tuples[0]
|
||||||
|
|
||||||
if tuple.Zone == "" || tuple.Zone == "*" {
|
// This logic would be required on a non-extended network:
|
||||||
tuple.Zone = port.DefaultZoneName
|
// if tuple.Zone == "" || tuple.Zone == "*" {
|
||||||
}
|
// tuple.Zone = port.DefaultZoneName
|
||||||
|
// }
|
||||||
|
|
||||||
zones := port.Router.ZoneTable.LookupName(tuple.Zone)
|
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
|
// But also...if we match the query, reply as though it was a LkUp
|
||||||
// This uses the *input* port information.
|
// This uses the *input* port information.
|
||||||
outDDP2, err := port.helloWorldThisIsMe(ddpkt, nbpkt.NBPID, tuple)
|
outDDP2, err := port.helloWorldThisIsMe(nbpkt.NBPID, tuple)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -117,8 +167,8 @@ func (port *EtherTalkPort) handleNBPBrRq(ctx context.Context, ddpkt *ddp.ExtPack
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Print("NBP: Replying to BrRq directly with LkUp-Reply for myself")
|
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
|
// Can reply to this BrRq on the same port we got it, because it
|
||||||
// routed
|
// wasn't routed
|
||||||
if err := port.Send(ctx, outDDP2); err != nil {
|
if err := port.Send(ctx, outDDP2); err != nil {
|
||||||
return err
|
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.
|
// 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 != "=" {
|
if tuple.Object != "jrouter" && tuple.Object != "=" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -187,13 +237,23 @@ func (port *EtherTalkPort) helloWorldThisIsMe(ddpkt *ddp.ExtPacket, nbpID uint8,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't marshal LkUp-Reply: %v", err)
|
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{
|
return &ddp.ExtPacket{
|
||||||
ExtHeader: ddp.ExtHeader{
|
ExtHeader: ddp.ExtHeader{
|
||||||
Size: uint16(len(respRaw)) + atalk.DDPExtHeaderSize,
|
Size: uint16(len(respRaw)) + atalk.DDPExtHeaderSize,
|
||||||
Cksum: 0,
|
Cksum: 0,
|
||||||
DstNet: ddpkt.SrcNet,
|
DstNet: tuple.Network,
|
||||||
DstNode: ddpkt.SrcNode,
|
DstNode: tuple.Node,
|
||||||
DstSocket: ddpkt.SrcSocket,
|
DstSocket: tuple.Socket,
|
||||||
SrcNet: port.MyAddr.Network,
|
SrcNet: port.MyAddr.Network,
|
||||||
SrcNode: port.MyAddr.Node,
|
SrcNode: port.MyAddr.Node,
|
||||||
SrcSocket: 2,
|
SrcSocket: 2,
|
||||||
|
|
|
@ -19,13 +19,12 @@ package router
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
|
|
||||||
"gitea.drjosh.dev/josh/jrouter/atalk/nbp"
|
"gitea.drjosh.dev/josh/jrouter/atalk/nbp"
|
||||||
"github.com/sfiera/multitalk/pkg/ddp"
|
"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 {
|
if ddpkt.Proto != ddp.ProtoNBP {
|
||||||
return fmt.Errorf("invalid DDP type %d on socket 2", ddpkt.Proto)
|
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??
|
// It's something else??
|
||||||
return fmt.Errorf("can't handle %v", nbpkt.Function)
|
return fmt.Errorf("can't handle %v", nbpkt.Function)
|
||||||
}
|
}
|
||||||
|
return rtr.handleNBPFwdReq(ctx, ddpkt, nbpkt)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,10 +84,10 @@ func (port *EtherTalkPort) Serve(ctx context.Context) {
|
||||||
log.Printf("Couldn't unmarshal DDP packet: %v", err)
|
log.Printf("Couldn't unmarshal DDP packet: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Printf("DDP: src (%d.%d s %d) dst (%d.%d s %d) proto %d data len %d",
|
// 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.SrcNet, ddpkt.SrcNode, ddpkt.SrcSocket,
|
||||||
ddpkt.DstNet, ddpkt.DstNode, ddpkt.DstSocket,
|
// ddpkt.DstNet, ddpkt.DstNode, ddpkt.DstSocket,
|
||||||
ddpkt.Proto, len(ddpkt.Data))
|
// ddpkt.Proto, len(ddpkt.Data))
|
||||||
|
|
||||||
// Glean address info for AMT, but only if SrcNet is our net
|
// 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
|
// (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 {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
port.MyAddr = myAddr.Proto
|
||||||
|
|
||||||
// Our network?
|
// Our network?
|
||||||
// "The network number 0 is reserved to mean unknown; by default
|
// "The network number 0 is reserved to mean unknown; by default
|
||||||
|
|
|
@ -27,6 +27,7 @@ type Router struct {
|
||||||
Config *Config
|
Config *Config
|
||||||
RouteTable *RouteTable
|
RouteTable *RouteTable
|
||||||
ZoneTable *ZoneTable
|
ZoneTable *ZoneTable
|
||||||
|
Ports []*EtherTalkPort
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward routes a packet towards the right destination.
|
// Forward routes a packet towards the right destination.
|
||||||
|
|
Loading…
Reference in a new issue