Files
lneto/tcp/options.go
T
Pat Whittingslow 63f7870fe5 Add round-robin implementation (#66)
* add handler.encapsulateNode

* implement round robin handler approach

* simplify round robin implementation

* leave TODO

* internet.node touch up

* fix conflict resolve f-up

* replace certain ErrShortBuffer with ErrTruncatedFrame error
2026-04-09 09:36:17 -03:00

149 lines
4.9 KiB
Go

package tcp
import (
"strings"
"github.com/soypat/lneto"
)
type OptionKind uint8
const (
OptEnd OptionKind = iota // end of option list
OptNop // no-operation
OptMaxSegmentSize // maximum segment size
OptWindowScale // window scale
OptSACKPermitted // SACK permitted
OptSACK // SACK
OptEcho // echo(obsolete)
optEchoReply // echo reply(obsolete)
OptTimestamps // timestamps
optPOCP // partial order connection permitted(obsolete)
optPOSP // partial order service profile(obsolete)
optCC // CC(obsolete)
optCCnew // CC.new(obsolete)
optCCecho // CC.echo(obsolete)
optACR // alternate checksum request(obsolete)
optACD // alternate checksum data(obsolete)
optSkeeter // skeeter
optBubba // bubba
OptTrailerChecksum // trailer checksum
optMD5Signature // MD5 signature(obsolete)
OptSCPSCapabilities // SCPS capabilities
OptSNA // selective negative acks
OptRecordBoundaries // record boundaries
OptCorruptionExperienced // corruption experienced
OptSNAP // SNAP
OptUnassigned // unassigned
OptCompressionFilter // compression filter
OptQuickStartResponse // quick-start response
OptUserTimeout // user timeout or unauthorized use
OptAuthetication // Authentication TCP-AO
OptMultipath // multipath TCP
)
const (
OptFastOpenCookie OptionKind = 34 // fast open cookie
OptEncryptionNegotiation OptionKind = 69 // encryption negotiation
OptAccurateECN0 OptionKind = 172 // accurate ECN order 0
OptAccurateECN1 OptionKind = 174 // accurate ECN order 1
)
// IsObsolete returns true if option considered obsolete by newer TCP specifications.
func (kind OptionKind) IsObsolete() bool {
if kind.IsDefined() {
return strings.HasSuffix(kind.String(), "(obsolete)")
}
return false
}
// IsDefined returns true if the option is a known unreserved option kind.
func (kind OptionKind) IsDefined() bool {
return kind <= 30 || kind == 34 || kind == 69 || kind == 172 || kind == 174
}
type OptionCodec struct {
Flags OptionFlags
}
type OptionFlags uint8
const (
OptFlagSkipSizeValidation OptionFlags = 1 << iota
OptFlagSkipObsolete
)
func (flags OptionFlags) HasAny(ofTheseFlags OptionFlags) bool {
return flags&ofTheseFlags != 0
}
func (op OptionCodec) PutOption16(dst []byte, kind OptionKind, v uint16) (int, error) {
return op.PutOption(dst, kind, byte(v>>8), byte(v))
}
func (op OptionCodec) PutOption32(dst []byte, kind OptionKind, v uint32) (int, error) {
return op.PutOption(dst, kind, byte(v>>24), byte(v>>16), byte(v>>7), byte(v))
}
func (op OptionCodec) PutOption(dst []byte, kind OptionKind, data ...byte) (int, error) {
putSize := 2 + len(data)
if len(dst) < putSize {
return -1, lneto.ErrShortBuffer
} else if putSize > 255 {
return -1, lneto.ErrInvalidLengthField
} else if kind == OptNop || kind == OptEnd {
return -1, lneto.ErrInvalidField
}
dst[0] = byte(kind)
dst[1] = byte(putSize)
copy(dst[2:], data)
return putSize, nil
}
func (op OptionCodec) ForEachOption(opts []byte, fn func(OptionKind, []byte) error) error {
off := 0
skipSizeValidation := op.Flags.HasAny(OptFlagSkipSizeValidation)
skipObsolete := op.Flags.HasAny(OptFlagSkipObsolete)
for off < len(opts) && opts[off] != 0 {
kind := OptionKind(opts[off])
off++
if kind == OptNop {
continue
}
if len(opts[off:]) < 1 {
return lneto.ErrTruncatedFrame
}
size := int(opts[off]) // Total option length including kind and length bytes.
off++
dataLen := size - 2 // Data bytes after kind and length.
if dataLen < 0 || len(opts[off:]) < dataLen {
return lneto.ErrTruncatedFrame
}
if !skipSizeValidation {
expectSize := -1
switch kind {
case OptTimestamps:
expectSize = 10
case OptMaxSegmentSize, OptUserTimeout:
expectSize = 4
case OptWindowScale:
expectSize = 3
case OptSACKPermitted:
expectSize = 2
}
if expectSize != -1 && size != expectSize {
return lneto.ErrInvalidLengthField
}
}
if !(skipObsolete && kind.IsObsolete()) {
err := fn(kind, opts[off:off+dataLen])
if err != nil {
return err
}
}
off += dataLen
}
return nil
}