jrouter/aurp/open.go
2024-03-22 14:20:31 +11:00

146 lines
3 KiB
Go

package aurp
import (
"encoding/binary"
"fmt"
"io"
)
// OpenReq is used to open a one-way connection between AIRs.
type OpenReqPacket struct {
Header
Version uint16 // currently always 1
Options Options
}
func (p *OpenReqPacket) WriteTo(w io.Writer) (int64, error) {
a := acc(w)
a.writeTo(&p.Header)
a.write16(p.Version)
a.writeTo(p.Options)
return a.ret()
}
func parseOpenReq(p []byte) (*OpenReqPacket, error) {
if len(p) < 3 {
return nil, fmt.Errorf("insufficient input length %d for Open-Req packet", len(p))
}
opts, err := parseOptions(p[2:])
if err != nil {
return nil, err
}
return &OpenReqPacket{
Version: binary.BigEndian.Uint16(p[:2]),
Options: opts,
}, nil
}
// OpenRsp is used to respond to Open-Req.
type OpenRspPacket struct {
Header
RateOrErrCode int16
Options Options
}
func (p *OpenRspPacket) WriteTo(w io.Writer) (int64, error) {
a := acc(w)
a.writeTo(&p.Header)
a.write16(uint16(p.RateOrErrCode))
a.writeTo(p.Options)
return a.ret()
}
func parseOpenRsp(p []byte) (*OpenRspPacket, error) {
if len(p) < 3 {
return nil, fmt.Errorf("insufficient input length %d for Open-Rsp packet", len(p))
}
opts, err := parseOptions(p[2:])
if err != nil {
return nil, err
}
return &OpenRspPacket{
RateOrErrCode: int16(binary.BigEndian.Uint16(p[:2])),
Options: opts,
}, nil
}
// OptionTuple is used to pass option information in Open-Req and Open-Rsp
// packets.
type OptionTuple struct {
// Length uint8 = 1(for Type) + len(Data)
Type OptionType
Data []byte
}
func (ot *OptionTuple) WriteTo(w io.Writer) (int64, error) {
if len(ot.Data) > 254 {
return 0, fmt.Errorf("option tuple data too long [%d > 254]", len(ot.Data))
}
a := acc(w)
a.write([]byte{
byte(len(ot.Data) + 1),
byte(ot.Type),
})
a.write(ot.Data)
return a.ret()
}
func parseOptionTuple(p []byte) (OptionTuple, []byte, error) {
if len(p) < 2 {
return OptionTuple{}, p, fmt.Errorf("insufficient input length %d for option tuple", len(p))
}
olen := int(p[0]) + 1
if len(p) < olen {
return OptionTuple{}, p, fmt.Errorf("insufficient input for option tuple data length %d", olen)
}
return OptionTuple{
Type: OptionType(p[1]),
Data: p[2:olen],
}, p[olen:], nil
}
// OptionType is used to distinguish different options.
type OptionType uint8
// Various option types
const (
OptionTypeAuthentication OptionType = 0x01
// All other types reserved
)
type Options []OptionTuple
func (o Options) WriteTo(w io.Writer) (int64, error) {
if len(o) > 255 {
return 0, fmt.Errorf("too many options [%d > 255]", len(o))
}
a := acc(w)
a.write8(uint8(len(o)))
for _, ot := range o {
a.writeTo(&ot)
}
return a.ret()
}
func parseOptions(p []byte) (Options, error) {
if len(p) < 1 {
return nil, fmt.Errorf("insufficint input length %d for options", len(p))
}
optc := p[0]
opts := make([]OptionTuple, optc)
for i := range optc {
ot, np, err := parseOptionTuple(p)
if err != nil {
return nil, fmt.Errorf("parsing option %d: %w", i, err)
}
opts[i] = ot
p = np
}
// TODO: warn about trailing data?
return opts, nil
}