Implement zone multicast

This commit is contained in:
Josh Deprez 2024-04-15 20:48:40 +10:00
parent 0d12b55735
commit ed233b3f3e
Signed by: josh
SSH key fingerprint: SHA256:zZji7w1Ilh2RuUpbQcqkLPrqmRwpiCSycbF2EfKm6Kw
6 changed files with 121 additions and 9 deletions

102
atalk/strings.go Normal file
View file

@ -0,0 +1,102 @@
/*
Copyright 2024 Josh Deprez
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package atalk
import (
"math/bits"
"github.com/sfiera/multitalk/pkg/ethernet"
)
// Inside AppleTalk, appendix D
var toUpperMap = []byte{
// The alphabet
0x61: 0x41,
0x62: 0x42,
0x63: 0x43,
0x64: 0x44,
0x65: 0x45,
0x66: 0x46,
0x67: 0x47,
0x68: 0x48,
0x69: 0x49,
0x6A: 0x4A,
0x6B: 0x4B,
0x6C: 0x4C,
0x6D: 0x4D,
0x6E: 0x4E,
0x6F: 0x4F,
0x70: 0x50,
0x71: 0x51,
0x72: 0x52,
0x73: 0x53,
0x74: 0x54,
0x75: 0x55,
0x76: 0x56,
0x77: 0x57,
0x78: 0x58,
0x79: 0x59,
0x7A: 0x5A,
// Letters with diacritics, etc
0x88: 0xCB,
0x8A: 0x80,
0x8B: 0xCC,
0x8C: 0x81,
0x8D: 0x82,
0x8E: 0x83,
0x96: 0x84,
0x9A: 0x85,
0x9B: 0xCD,
0x9F: 0x86,
0xBE: 0xAE,
0xBF: 0xAF,
0xCF: 0xCE,
}
func Checksum(s string) uint16 {
// Inside AppleTalk, pp 4-17 and pp 8-18
var cksum uint16
for _, b := range []byte(s) {
cksum += uint16(b)
cksum = bits.RotateLeft16(cksum, -1)
}
if cksum == 0 {
cksum = 0xFFFF
}
return cksum
}
func ToUpper(s string) string {
// Inside Appletalk, appendix D
sb := []byte(s)
out := make([]byte, len(sb))
for i, b := range sb {
if u := toUpperMap[b]; u != 0 {
out[i] = u
} else {
out[i] = b
}
}
return string(out)
}
func MulticastAddr(zone string) ethernet.Addr {
// Inside AppleTalk, pp 3-10 and pp 8-18
h := Checksum(ToUpper(zone))
return ethernet.Addr{0x09, 0x00, 0x07, 0x00, 0x00, byte(h % 0xFD)}
}

View file

@ -57,9 +57,9 @@ type GetNetInfoReplyPacket struct {
// DDP type = 6 // DDP type = 6
// --- // ---
// ZIP command = 6 // ZIP command = 6
ZoneInvalid bool // 0x80 ZoneInvalid bool // 0x80 - "set if the zone name in the request is invalid for the network from which the request was sent"
UseBroadcast bool // 0x40 UseBroadcast bool // 0x40 - "set for data links that do not support multicast"
OnlyOneZone bool // 0x20 OnlyOneZone bool // 0x20 - "set if the network's zone list contains only one zone name"
// Remainder of flags reserved // Remainder of flags reserved
NetStart ddp.Network NetStart ddp.Network
NetEnd ddp.Network NetEnd ddp.Network

View file

@ -205,6 +205,9 @@ func main() {
continue continue
} }
// TODO: filter ethFrame.Dst to myHWAddr, the broadcast address,
// or the relevant zone multicast address
switch ethFrame.SNAPProto { switch ethFrame.SNAPProto {
case ethertalk.AARPProto: case ethertalk.AARPProto:
// log.Print("Got an AARP frame") // log.Print("Got an AARP frame")

6
nbp.go
View file

@ -100,6 +100,10 @@ func handleNBP(pcapHandle *pcap.Handle, myHWAddr, srcHWAddr ethernet.Addr, myAdd
case nbp.FunctionBrRq: case nbp.FunctionBrRq:
// There must be 1! // There must be 1!
tuple := &nbpkt.Tuples[0] tuple := &nbpkt.Tuples[0]
ethDst := ethertalk.AppleTalkBroadcast
if tuple.Zone != "*" && tuple.Zone != "" {
ethDst = atalk.MulticastAddr(tuple.Zone)
}
zones := zoneTable.LookupName(tuple.Zone) zones := zoneTable.LookupName(tuple.Zone)
for _, z := range zones { for _, z := range zones {
@ -112,7 +116,6 @@ func handleNBP(pcapHandle *pcap.Handle, myHWAddr, srcHWAddr ethernet.Addr, myAdd
// lookups in their own zone will receive LkUp packets from themselves // lookups in their own zone will receive LkUp packets from themselves
// (actually sent by a router). The node's NBP process should expect to // (actually sent by a router). The node's NBP process should expect to
// receive these packets and must reply to them." // receive these packets and must reply to them."
// TODO: use zone-specific multicast
nbpkt.Function = nbp.FunctionLkUp nbpkt.Function = nbp.FunctionLkUp
nbpRaw, err := nbpkt.Marshal() nbpRaw, err := nbpkt.Marshal()
if err != nil { if err != nil {
@ -128,6 +131,7 @@ func handleNBP(pcapHandle *pcap.Handle, myHWAddr, srcHWAddr ethernet.Addr, myAdd
if err != nil { if err != nil {
return err return err
} }
outFrame.Dst = ethDst
outFrameRaw, err := ethertalk.Marshal(*outFrame) outFrameRaw, err := ethertalk.Marshal(*outFrame)
if err != nil { if err != nil {
return err return err

View file

@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"log" "log"
"gitea.drjosh.dev/josh/jrouter/atalk"
"gitea.drjosh.dev/josh/jrouter/atalk/nbp" "gitea.drjosh.dev/josh/jrouter/atalk/nbp"
"github.com/google/gopacket/pcap" "github.com/google/gopacket/pcap"
"github.com/sfiera/multitalk/pkg/ddp" "github.com/sfiera/multitalk/pkg/ddp"
@ -43,11 +44,11 @@ func handleNBPInAURP(pcapHandle *pcap.Handle, myHWAddr ethernet.Addr, ddpkt *ddp
if len(nbpkt.Tuples) < 1 { if len(nbpkt.Tuples) < 1 {
return fmt.Errorf("no tuples in NBP packet") return fmt.Errorf("no tuples in NBP packet")
} }
tuple := &nbpkt.Tuples[0]
log.Printf("NBP/DDP/AURP: Converting FwdReq to LkUp (%v)", nbpkt.Tuples[0]) log.Printf("NBP/DDP/AURP: Converting FwdReq to LkUp (%v)", tuple)
// Convert it to a LkUp and broadcast on EtherTalk // Convert it to a LkUp and broadcast on EtherTalk
// TODO: use zone-specific multicast
nbpkt.Function = nbp.FunctionLkUp nbpkt.Function = nbp.FunctionLkUp
nbpRaw, err := nbpkt.Marshal() nbpRaw, err := nbpkt.Marshal()
if err != nil { if err != nil {
@ -66,7 +67,9 @@ func handleNBPInAURP(pcapHandle *pcap.Handle, myHWAddr ethernet.Addr, ddpkt *ddp
if err != nil { if err != nil {
return err return err
} }
// TODO: outFrame.Dst = zone-specific multicast address if tuple.Zone != "*" && tuple.Zone != "" {
outFrame.Dst = atalk.MulticastAddr(tuple.Zone)
}
outFrameRaw, err := ethertalk.Marshal(*outFrame) outFrameRaw, err := ethertalk.Marshal(*outFrame)
if err != nil { if err != nil {
return err return err

4
zip.go
View file

@ -126,12 +126,12 @@ func handleZIP(pcapHandle *pcap.Handle, srcHWAddr, myHWAddr ethernet.Addr, myAdd
// Only running a network with one zone for now. // Only running a network with one zone for now.
gnir := &zip.GetNetInfoReplyPacket{ gnir := &zip.GetNetInfoReplyPacket{
ZoneInvalid: zipkt.ZoneName != cfg.EtherTalk.ZoneName, ZoneInvalid: zipkt.ZoneName != cfg.EtherTalk.ZoneName,
UseBroadcast: true, // TODO: add multicast addr computation UseBroadcast: false,
OnlyOneZone: true, OnlyOneZone: true,
NetStart: cfg.EtherTalk.NetStart, NetStart: cfg.EtherTalk.NetStart,
NetEnd: cfg.EtherTalk.NetEnd, NetEnd: cfg.EtherTalk.NetEnd,
ZoneName: zipkt.ZoneName, // has to match request ZoneName: zipkt.ZoneName, // has to match request
MulticastAddr: ethertalk.AppleTalkBroadcast, MulticastAddr: atalk.MulticastAddr(cfg.EtherTalk.ZoneName),
DefaultZoneName: cfg.EtherTalk.ZoneName, DefaultZoneName: cfg.EtherTalk.ZoneName,
} }
log.Printf("ZIP: Replying with GetNetInfo-Reply: %+v", gnir) log.Printf("ZIP: Replying with GetNetInfo-Reply: %+v", gnir)