jrouter/aurp/aurp.go

282 lines
6.4 KiB
Go
Raw Permalink Normal View History

2024-03-31 09:31:50 +11:00
/*
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.
*/
2024-03-10 11:57:03 +11:00
// Package aurp implements types for encoding and decoding AppleTalk
// Update-Based Routing Protocol (AURP, RFC 1504) messages.
package aurp
import (
"encoding/binary"
"fmt"
2024-03-15 15:17:21 +11:00
"io"
2024-03-10 11:57:03 +11:00
)
2024-03-15 15:17:21 +11:00
// Header represents an AURP packet header. It includes the AURP-Tr header,
// which includes the domain header.
2024-03-10 11:57:03 +11:00
type Header struct {
2024-03-17 13:31:26 +11:00
TrHeader
2024-03-15 15:17:21 +11:00
2024-03-10 11:57:03 +11:00
CommandCode CmdCode
Flags RoutingFlag
}
2024-03-15 15:17:21 +11:00
// WriteTo writes the encoded form of the header to w.
func (h *Header) WriteTo(w io.Writer) (int64, error) {
a := acc(w)
2024-03-17 13:31:26 +11:00
a.writeTo(&h.TrHeader)
2024-03-15 15:17:21 +11:00
a.write16(uint16(h.CommandCode))
a.write16(uint16(h.Flags))
return a.ret()
}
2024-03-17 13:31:26 +11:00
func parseHeader(p []byte) (Header, []byte, error) {
2024-03-15 16:15:24 +11:00
if len(p) < 4 {
2024-03-17 13:31:26 +11:00
return Header{}, p, fmt.Errorf("insufficient input length %d for header", len(p))
2024-03-15 16:15:24 +11:00
}
2024-03-17 13:31:26 +11:00
return Header{
2024-03-15 16:15:24 +11:00
CommandCode: CmdCode(binary.BigEndian.Uint16(p[:2])),
Flags: RoutingFlag(binary.BigEndian.Uint16(p[2:4])),
}, p[4:], nil
}
2024-03-10 11:57:03 +11:00
// CmdCode is the command code used in AURP packets.
type CmdCode uint16
// Various command codes.
const (
CmdCodeRIReq CmdCode = 0x0001
CmdCodeRIRsp CmdCode = 0x0002
CmdCodeRIAck CmdCode = 0x0003
CmdCodeRIUpd CmdCode = 0x0004
CmdCodeRD CmdCode = 0x0005
CmdCodeZoneReq CmdCode = 0x0006 // has subcodes
CmdCodeZoneRsp CmdCode = 0x0007 // has subcodes
CmdCodeOpenReq CmdCode = 0x0008
CmdCodeOpenRsp CmdCode = 0x0009
CmdCodeTickle CmdCode = 0x000e
CmdCodeTickleAck CmdCode = 0x000f
)
// RoutingFlag is used in the flags field
type RoutingFlag uint16
const (
2024-03-22 14:20:31 +11:00
// Open-Req and RI-Req (SUI flags)
2024-03-10 11:57:03 +11:00
RoutingFlagSUINA RoutingFlag = 0x4000
RoutingFlagSUINDOrNRC RoutingFlag = 0x2000
RoutingFlagSUINDC RoutingFlag = 0x1000
RoutingFlagSUIZC RoutingFlag = 0x0800
2024-03-22 14:20:31 +11:00
// The combination of the above four flags (the SUI flags).
RoutingFlagAllSUI RoutingFlag = 0x7800
2024-03-10 11:57:03 +11:00
// RI-Rsp and GDZL-Rsp
RoutingFlagLast RoutingFlag = 0x8000
2024-03-22 14:20:31 +11:00
// Open-Rsp (environment flags)
2024-03-10 11:57:03 +11:00
RoutingFlagRemappingActive RoutingFlag = 0x4000
RoutingFlagHopCountReduction RoutingFlag = 0x2000
RoutingFlagReservedEnv RoutingFlag = 0x1800
// RI-Ack
RoutingFlagSendZoneInfo RoutingFlag = 0x4000
)
2024-03-15 15:17:21 +11:00
// Packet represents a full AURP packet, not including UDP or lower layers, but
// including the domain header and higher layers.
type Packet interface {
io.WriterTo
}
2024-04-05 10:43:29 +11:00
// Inc increments a uint16. It avoids 0 (65535 + 1 = 1).
func Inc(p *uint16) {
*p++
if *p == 0 {
*p++
}
}
2024-03-15 16:15:24 +11:00
// ParsePacket parses the body of a UDP packet for a domain header, and then
// based on the packet type, an AURP-Tr header, an AURP routing header, and
// then a particular packet type.
//
// (This function contains the big switch statement.)
2024-03-24 21:31:11 +11:00
func ParsePacket(p []byte) (DomainHeader, Packet, error) {
2024-03-22 16:14:55 +11:00
dh, p, err := ParseDomainHeader(p)
2024-03-15 16:15:24 +11:00
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-15 16:15:24 +11:00
}
if dh.Version != 1 {
2024-03-24 21:31:11 +11:00
return dh, nil, fmt.Errorf("unsupported domain header version %d", dh.Version)
2024-03-15 16:15:24 +11:00
}
switch dh.PacketType {
case PacketTypeAppleTalk:
2024-03-24 21:31:11 +11:00
return dh, &AppleTalkPacket{
2024-03-15 16:15:24 +11:00
DomainHeader: dh,
Data: p,
}, nil
case PacketTypeRouting:
tr, p, err := parseTrHeader(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-15 16:15:24 +11:00
}
tr.DomainHeader = dh
h, p, err := parseHeader(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-15 16:15:24 +11:00
}
h.TrHeader = tr
switch h.CommandCode {
case CmdCodeOpenReq:
oreq, err := parseOpenReq(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-15 16:15:24 +11:00
}
oreq.Header = h
2024-03-24 21:31:11 +11:00
return dh, oreq, nil
2024-03-15 16:15:24 +11:00
case CmdCodeOpenRsp:
orsp, err := parseOpenRsp(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-15 16:15:24 +11:00
}
orsp.Header = h
2024-03-24 21:31:11 +11:00
return dh, orsp, nil
2024-03-15 16:38:02 +11:00
2024-03-15 16:49:53 +11:00
case CmdCodeRIReq:
2024-03-24 21:31:11 +11:00
return dh, &RIReqPacket{
2024-03-15 16:49:53 +11:00
Header: h,
}, nil
case CmdCodeRIRsp:
2024-03-17 12:56:56 +11:00
rir, err := parseRIRsp(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-17 12:56:56 +11:00
}
rir.Header = h
2024-03-24 21:31:11 +11:00
return dh, rir, nil
2024-03-15 16:49:53 +11:00
case CmdCodeRIAck:
2024-03-24 21:31:11 +11:00
return dh, &RIAckPacket{
2024-03-15 16:49:53 +11:00
Header: h,
}, nil
2024-03-16 22:38:20 +11:00
case CmdCodeRIUpd:
riu, err := parseRIUpd(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-16 22:38:20 +11:00
}
riu.Header = h
2024-03-24 21:31:11 +11:00
return dh, riu, nil
2024-03-16 22:38:20 +11:00
2024-03-17 13:35:50 +11:00
case CmdCodeRD:
rd, err := parseRD(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-17 13:35:50 +11:00
}
rd.Header = h
2024-03-24 21:31:11 +11:00
return dh, rd, nil
2024-03-17 13:35:50 +11:00
2024-03-17 18:19:36 +11:00
case CmdCodeZoneReq:
sc, p, err := parseSubcode(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-17 18:19:36 +11:00
}
switch sc {
2024-03-17 21:08:18 +11:00
case SubcodeZoneInfoReq:
2024-03-17 18:19:36 +11:00
zir, err := parseZIReqPacket(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-17 18:19:36 +11:00
}
zir.Header = h
2024-03-24 21:31:11 +11:00
return dh, zir, nil
2024-03-17 18:19:36 +11:00
2024-03-17 21:08:18 +11:00
case SubcodeGetDomainZoneList:
gdzl, err := parseGDZLReqPacket(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-17 21:08:18 +11:00
}
gdzl.Header = h
2024-03-24 21:31:11 +11:00
return dh, gdzl, nil
2024-03-17 18:19:36 +11:00
2024-03-17 21:08:18 +11:00
case SubcodeGetZonesNet:
gzn, err := parseGZNReqPacket(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-17 21:08:18 +11:00
}
gzn.Header = h
2024-03-24 21:31:11 +11:00
return dh, gzn, nil
2024-03-17 18:19:36 +11:00
default:
2024-03-24 21:31:11 +11:00
return dh, nil, fmt.Errorf("unknown subcode %d", sc)
2024-03-17 18:19:36 +11:00
}
case CmdCodeZoneRsp:
sc, p, err := parseSubcode(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-17 18:19:36 +11:00
}
switch sc {
2024-03-17 21:08:18 +11:00
case SubcodeZoneInfoNonExt, SubcodeZoneInfoExt:
2024-03-17 18:19:36 +11:00
zir, err := parseZIRspPacket(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-17 18:19:36 +11:00
}
zir.Header = h
2024-03-17 21:08:18 +11:00
zir.Subcode = sc // 1 or 2, only known at this layer
2024-03-24 21:31:11 +11:00
return dh, zir, nil
2024-03-17 18:19:36 +11:00
2024-03-17 21:08:18 +11:00
case SubcodeGetDomainZoneList:
gdzl, err := parseGDZLRspPacket(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-17 21:08:18 +11:00
}
gdzl.Header = h
2024-03-24 21:31:11 +11:00
return dh, gdzl, nil
2024-03-17 18:19:36 +11:00
2024-03-17 21:08:18 +11:00
case SubcodeGetZonesNet:
gzn, err := parseGZNRspPacket(p)
if err != nil {
2024-03-24 21:31:11 +11:00
return dh, nil, err
2024-03-17 21:08:18 +11:00
}
gzn.Header = h
2024-03-24 21:31:11 +11:00
return dh, gzn, nil
2024-03-17 18:19:36 +11:00
default:
2024-03-24 21:31:11 +11:00
return dh, nil, fmt.Errorf("unknown subcode %d", sc)
2024-03-17 18:19:36 +11:00
}
case CmdCodeTickle:
2024-03-24 21:31:11 +11:00
return dh, &TicklePacket{
2024-03-17 18:19:36 +11:00
Header: h,
}, nil
case CmdCodeTickleAck:
2024-03-24 21:31:11 +11:00
return dh, &TickleAckPacket{
2024-03-17 18:19:36 +11:00
Header: h,
}, nil
2024-03-15 16:38:02 +11:00
default:
2024-03-24 21:31:11 +11:00
return dh, nil, fmt.Errorf("unknown routing packet command code %d", h.CommandCode)
2024-03-15 16:15:24 +11:00
}
default:
2024-03-24 21:31:11 +11:00
return dh, nil, fmt.Errorf("unsupported domain header packet type %d", dh.PacketType)
2024-03-15 16:15:24 +11:00
}
2024-03-15 15:17:21 +11:00
}