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
// ---
// ZIP command = 6
ZoneInvalid bool // 0x80
UseBroadcast bool // 0x40
OnlyOneZone bool // 0x20
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 - "set for data links that do not support multicast"
OnlyOneZone bool // 0x20 - "set if the network's zone list contains only one zone name"
// Remainder of flags reserved
NetStart ddp.Network
NetEnd ddp.Network

View file

@ -205,6 +205,9 @@ 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")

6
nbp.go
View file

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

View file

@ -20,6 +20,7 @@ import (
"fmt"
"log"
"gitea.drjosh.dev/josh/jrouter/atalk"
"gitea.drjosh.dev/josh/jrouter/atalk/nbp"
"github.com/google/gopacket/pcap"
"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 {
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
// TODO: use zone-specific multicast
nbpkt.Function = nbp.FunctionLkUp
nbpRaw, err := nbpkt.Marshal()
if err != nil {
@ -66,7 +67,9 @@ func handleNBPInAURP(pcapHandle *pcap.Handle, myHWAddr ethernet.Addr, ddpkt *ddp
if err != nil {
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)
if err != nil {
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.
gnir := &zip.GetNetInfoReplyPacket{
ZoneInvalid: zipkt.ZoneName != cfg.EtherTalk.ZoneName,
UseBroadcast: true, // TODO: add multicast addr computation
UseBroadcast: false,
OnlyOneZone: true,
NetStart: cfg.EtherTalk.NetStart,
NetEnd: cfg.EtherTalk.NetEnd,
ZoneName: zipkt.ZoneName, // has to match request
MulticastAddr: ethertalk.AppleTalkBroadcast,
MulticastAddr: atalk.MulticastAddr(cfg.EtherTalk.ZoneName),
DefaultZoneName: cfg.EtherTalk.ZoneName,
}
log.Printf("ZIP: Replying with GetNetInfo-Reply: %+v", gnir)