Fix ZIP ATP replies

This commit is contained in:
Josh Deprez 2024-04-19 12:21:33 +10:00
parent e95df3db20
commit c1efd9f91b
No known key found for this signature in database
5 changed files with 56 additions and 7 deletions

View file

@ -22,8 +22,9 @@ Things I plan to fix Real Soon Now:
interface. I haven't tested it with netatalk or tashrouter on the same
host, but I think using a distinct Ethernet address would help them coexist.
* It doesn't do any of the required packet splitting to keep packets under the
AppleTalk size limits. In particular ZIP GetZoneList Replies are incorrect
when the zone list would exceed the limit.
AppleTalk size limits. ~~In particular ZIP GetZoneList Replies are incorrect
when the zone list would exceed the limit.~~ GetZoneList is now fixed, but
various others need fixing.
* It logs a lot and has no other monitoring or observability capability. I plan
to add a Prometheus metrics endpoint and at least add log levels / verbosity
flag.

View file

@ -20,6 +20,13 @@ import (
"bytes"
"encoding/binary"
"fmt"
"gitea.drjosh.dev/josh/jrouter/atalk"
)
const (
HeaderSize = 8
MaxDataSize = atalk.DDPMaxDataSize - HeaderSize // 578
)
const (
@ -50,6 +57,9 @@ func (p *TReq) Marshal() ([]byte, error) {
if p.TRelTimeoutIndicator > 4 {
return nil, fmt.Errorf("invalid TRel timeout indicator [%d > 4]", p.TRelTimeoutIndicator)
}
if len(p.Data) > MaxDataSize {
return nil, fmt.Errorf("%w [%d > %d]", atalk.ErrDataTooBig, len(p.Data), MaxDataSize)
}
b := bytes.NewBuffer(nil)
ci := byte(ciFuncTReq)

View file

@ -16,4 +16,11 @@
package atalk
const DDPExtHeaderSize = 13
import "errors"
const (
DDPExtHeaderSize = 13
DDPMaxDataSize = 586
)
var ErrDataTooBig = errors.New("data too big")

View file

@ -21,6 +21,7 @@ import (
"encoding/binary"
"fmt"
"gitea.drjosh.dev/josh/jrouter/atalk"
"gitea.drjosh.dev/josh/jrouter/atalk/atp"
)
@ -78,6 +79,9 @@ func (p *GetZonesReplyPacket) MarshalTResp() (*atp.TResp, error) {
b.WriteString(z)
}
r.Data = b.Bytes()
if len(r.Data) > atp.MaxDataSize {
return nil, fmt.Errorf("%w [%d > %d]", atalk.ErrDataTooBig, len(r.Data), atp.MaxDataSize)
}
return r, nil
}

35
zip.go
View file

@ -32,7 +32,7 @@ import (
func handleZIP(pcapHandle *pcap.Handle, srcHWAddr, myHWAddr ethernet.Addr, myAddr aarp.AddrPair, cfg *config, zones *ZoneTable, ddpkt *ddp.ExtPacket) error {
switch ddpkt.Proto {
case 3: // ATP
case ddp.ProtoATP:
atpkt, err := atp.UnmarshalPacket(ddpkt.Data)
if err != nil {
return err
@ -43,11 +43,15 @@ func handleZIP(pcapHandle *pcap.Handle, srcHWAddr, myHWAddr ethernet.Addr, myAdd
if err != nil {
return err
}
// TODO: handle this in a more transactiony way
if gzl.StartIndex == 0 {
return fmt.Errorf("ZIP ATP: received request with StartIndex = 0 (invalid)")
}
resp := &zip.GetZonesReplyPacket{
TID: gzl.TID,
LastFlag: true, // TODO: support multiple response packets
LastFlag: true,
}
switch gzl.Function {
case zip.FunctionGetZoneList:
resp.Zones = zones.AllNames()
@ -59,6 +63,29 @@ func handleZIP(pcapHandle *pcap.Handle, srcHWAddr, myHWAddr ethernet.Addr, myAdd
resp.Zones = []string{cfg.EtherTalk.ZoneName}
}
// Inside AppleTalk SE, pp 8-8
if int(gzl.StartIndex) > len(resp.Zones) {
// "Note: A 0-byte response will be returned by a router if the
// index specified in the request is greater than the index of
// the last zone in the list (and the user bytes field will
// indicate no more zones)."
resp.Zones = nil
} else {
// Trim the zones list
// "zone names in the router are assumed to be numbered starting
// with 1"
resp.Zones = resp.Zones[gzl.StartIndex-1:]
size := 0
for i, z := range resp.Zones {
size += 1 + len(z) // length prefix plus string
if size > atp.MaxDataSize {
resp.LastFlag = false
resp.Zones = resp.Zones[:i]
break
}
}
}
respATP, err := resp.MarshalTResp()
if err != nil {
return err
@ -99,7 +126,7 @@ func handleZIP(pcapHandle *pcap.Handle, srcHWAddr, myHWAddr ethernet.Addr, myAdd
return fmt.Errorf("unsupported ATP packet type %T for ZIP", atpkt)
}
case 6: // ZIP
case ddp.ProtoZIP:
zipkt, err := zip.UnmarshalPacket(ddpkt.Data)
if err != nil {
return err