ltp

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 2, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package ltp implements the Licklider Transmission Protocol per RFC 5326, profiled for space links by CCSDS 734.1-B-1.

LTP moves blocks of data over links where a round trip takes minutes or hours. TCP's handshakes are useless at those delays, so LTP takes a different approach: the sender pushes the whole block, then asks "what did you miss?" at checkpoints, and the receiver answers with reception claims.

A block has two parts. The red part is delivered reliably: gaps are retransmitted until the receiver confirms it has everything. The green part is best effort, sent once and never chased. A block can be all red, all green, or red followed by green.

block:  [ ────── red part ────── │ ─── green part ─── ]
          retransmitted on loss     sent once

Nearly every field is a Self-Delimiting Numeric Value, so this package builds on pkg/sdnv.

The session machines here own no goroutines and no clock, the same contract as pkg/cop's FOP-1. LTP's timers (checkpoint retransmission, report retransmission, cancel retransmission) are the caller's to run, because on a light-minutes link only the mission knows what a sensible timeout is.

Index

Constants

View Source
const (
	// ExtensionAuth is the LTP authentication extension.
	ExtensionAuth uint8 = 0x00
	// ExtensionCookie is the LTP cookie extension.
	ExtensionCookie uint8 = 0x01
)

Extension tags from the IANA LTP Extension Tag registry (clause 3.1.4).

View Source
const DefaultMaxBlockSize = 64 << 20

DefaultMaxBlockSize bounds a received block when ReceiverConfig leaves MaxBlockSize at zero: 64 MiB.

View Source
const DefaultSegmentSize = 1024

DefaultSegmentSize is the payload size used when SenderConfig leaves SegmentSize at zero.

View Source
const Version = 0

Version is the LTP segment version number, per RFC 5326 clause 3.1. Only 0 is defined.

Variables

View Source
var (
	// ErrDataTooShort indicates the input ended before a field it must contain.
	ErrDataTooShort = errors.New("data too short for the LTP field being read")

	// ErrInvalidVersion indicates a segment version other than 0, the only
	// value RFC 5326 clause 3.1 defines.
	ErrInvalidVersion = errors.New("invalid LTP version: only version 0 is defined")

	// ErrUndefinedSegmentType indicates one of the type codes RFC 5326 clause 3.1.2
	// marks undefined: 5, 6, 10 and 11.
	ErrUndefinedSegmentType = errors.New("undefined LTP segment type code")

	// ErrWrongSegmentType indicates a decoder was handed another type's bytes.
	ErrWrongSegmentType = errors.New("segment type does not match the decoder")

	// ErrTooManyExtensions indicates more than 15 header or trailer
	// extensions, which the 4-bit counts of clause 3.1.4 cannot describe.
	ErrTooManyExtensions = errors.New("more than 15 extensions in a header or trailer")

	// ErrInvalidSerialNumber indicates a checkpoint or report serial number of
	// zero where clause 3.2.1 and clause 3.2.2 forbid it.
	ErrInvalidSerialNumber = errors.New("checkpoint and report serial numbers must not be zero")

	// ErrInvalidBounds indicates a report segment whose upper bound is below
	// its lower bound.
	ErrInvalidBounds = errors.New("report segment upper bound is below its lower bound")

	// ErrInvalidClaim indicates a reception claim of zero length, or one
	// reaching past the report's upper bound (clause 3.2.2).
	ErrInvalidClaim = errors.New("invalid reception claim")

	// ErrInvalidReasonCode indicates a cancel reason code in the reserved
	// range 06 to FF (clause 3.2.4).
	ErrInvalidReasonCode = errors.New("invalid cancel reason code")

	// ErrSessionClosed indicates an operation on a session that has already
	// finished or been cancelled.
	ErrSessionClosed = errors.New("session is closed")

	// ErrBlockTooLarge indicates a data segment naming a block position past
	// the receiver's configured maximum. A segment offset is an SDNV and can
	// reach 2^64, so a cap is what stops one bad segment exhausting memory.
	ErrBlockTooLarge = errors.New("data segment reaches past the maximum block size")

	// ErrRedGreenOrder indicates green-part data below a red-part offset, or
	// red-part data above a green-part offset, which clause 3.2.4 calls MISCOLORED.
	ErrRedGreenOrder = errors.New("miscolored block: red and green parts overlap out of order")
)

Sentinel errors returned by the LTP codecs and session machines.

Functions

This section is empty.

Types

type CancelReason

type CancelReason uint8

CancelReason is the one-octet reason code of clause 3.2.4.

const (
	// ReasonUserCancelled means the client service cancelled the session.
	ReasonUserCancelled CancelReason = 0x00
	// ReasonUnreachable means the client service could not be reached.
	ReasonUnreachable CancelReason = 0x01
	// ReasonRetransmitLimit means the retransmission limit was exceeded.
	ReasonRetransmitLimit CancelReason = 0x02
	// ReasonMiscolored means red data arrived above a green offset, or green
	// data below a red one.
	ReasonMiscolored CancelReason = 0x03
	// ReasonSystemCancelled means a system error ended the session.
	ReasonSystemCancelled CancelReason = 0x04
	// ReasonRetransmitCyclesExceeded means the retransmission-cycles limit
	// was exceeded.
	ReasonRetransmitCyclesExceeded CancelReason = 0x05
)

func (CancelReason) String

func (c CancelReason) String() string

String names the reason.

func (CancelReason) Valid

func (c CancelReason) Valid() bool

Valid reports whether the reason code is one clause 3.2.4 defines. Codes 06 to FF are reserved.

type CancelSegment

type CancelSegment struct {
	Reason CancelReason
}

CancelSegment is the content of a cancel segment, per clause 3.2.4: a single reason-code octet.

func DecodeCancelSegment

func DecodeCancelSegment(data []byte) (*CancelSegment, int, error)

DecodeCancelSegment parses cancel content.

func (*CancelSegment) Encode

func (c *CancelSegment) Encode() ([]byte, error)

Encode serializes the cancel content.

func (*CancelSegment) Humanize

func (c *CancelSegment) Humanize() string

Humanize returns a human-readable summary.

type DataSegment

type DataSegment struct {
	// ClientServiceID names the upper-level service to deliver to. It works
	// like a TCP port number.
	ClientServiceID uint64
	// Offset is where this data belongs in the block, in octets from the start.
	Offset uint64
	// Data is the client service data itself. Its length travels as an SDNV.
	Data []byte

	// CheckpointSerial identifies this checkpoint among the sender's. Present
	// only on checkpoint segments, and never zero.
	CheckpointSerial uint64
	// ReportSerial is the serial of the report that prompted this checkpoint,
	// or zero when the checkpoint was not prompted by one.
	ReportSerial uint64
}

DataSegment is the content of a data segment, per RFC 5326 clause 3.2.1.

Checkpoint segments carry two extra serial numbers. Non-checkpoint segments must not: the spec is explicit that they "MUST continue on directly with the client service data".

func DecodeDataSegment

func DecodeDataSegment(data []byte, isCheckpoint bool) (*DataSegment, int, error)

DecodeDataSegment parses data segment content, returning the segment and the octets consumed.

func (*DataSegment) Encode

func (d *DataSegment) Encode(isCheckpoint bool) ([]byte, error)

Encode serializes the data segment content. isCheckpoint must match the segment type in the header, since the wire format is not self-describing.

func (*DataSegment) End

func (d *DataSegment) End() uint64

End returns the block offset just past this segment's last octet.

func (*DataSegment) Humanize

func (d *DataSegment) Humanize() string

Humanize returns a human-readable summary.

type Extension

type Extension struct {
	Tag   uint8
	Value []byte
}

Extension is one header or trailer extension TLV, per clause 3.1.4: a one-octet tag, an SDNV length, then the value.

func (Extension) Encode

func (e Extension) Encode() []byte

Encode serializes the extension TLV.

type Header struct {
	Type      SegmentType
	SessionID SessionID

	// HeaderExtensions sit between the counts octet and the segment content.
	HeaderExtensions []Extension
	// TrailerExtensions follow the segment content. They are counted in the
	// same octet as the header ones, which is why they live here.
	TrailerExtensions []Extension
}

Header is the part every LTP segment shares, per clause 3.1: a control octet carrying version and type, the session ID, an extension-counts octet, and the header extensions themselves.

func DecodeHeader

func DecodeHeader(data []byte) (*Header, int, int, error)

DecodeHeader parses a segment header from the front of data, returning the header, the number of octets consumed, and how many trailer extensions the counts octet promised.

func (*Header) Encode

func (h *Header) Encode() ([]byte, error)

Encode serializes the header: control octet, session ID, extension counts, then the header extensions. Trailer extensions are appended by Segment.Encode after the content.

func (*Header) Humanize

func (h *Header) Humanize() string

Humanize returns a human-readable summary of the header.

func (*Header) Validate

func (h *Header) Validate() error

Validate checks the header against clause 3.1.

type Receiver

type Receiver struct {
	// contains filtered or unexported fields
}

Receiver drives one incoming LTP session.

Like Sender it owns no goroutines and no clock: the caller feeds it segments with HandleSegment and asks NextSegment what to send back.

A Receiver is safe for concurrent use.

func NewReceiver

func NewReceiver(config ReceiverConfig) (*Receiver, error)

NewReceiver prepares a session to receive one block.

func (*Receiver) Block

func (r *Receiver) Block() []byte

Block returns the data received so far. It is complete only when Complete reports true.

func (*Receiver) Cancel

func (r *Receiver) Cancel(reason CancelReason) error

Cancel abandons the session from the receiver's end.

func (*Receiver) Complete

func (r *Receiver) Complete() bool

Complete reports whether the whole block has arrived, red and green.

func (*Receiver) Done

func (r *Receiver) Done() bool

Done reports whether the session has closed or been cancelled.

func (*Receiver) HandleSegment

func (r *Receiver) HandleSegment(seg *Segment) error

HandleSegment feeds one arriving segment into the session.

func (*Receiver) MissingRanges

func (r *Receiver) MissingRanges() []ReceptionClaim

MissingRanges returns the red-part ranges still outstanding, as block offsets.

func (*Receiver) NextSegment

func (r *Receiver) NextSegment() (*Segment, bool, error)

NextSegment returns the next segment to send back, or ok == false when nothing is pending.

func (*Receiver) RedPart

func (r *Receiver) RedPart() []byte

RedPart returns the reliably delivered prefix of the block.

func (*Receiver) RedPartComplete

func (r *Receiver) RedPartComplete() bool

RedPartComplete reports whether every octet of the red part has arrived.

func (*Receiver) RequestReport

func (r *Receiver) RequestReport()

RequestReport queues an asynchronous report, one not prompted by a checkpoint. Clause 3.2.2 gives it a checkpoint serial of zero. The caller drives this from its own timer.

func (*Receiver) State

func (r *Receiver) State() SessionState

State returns the session state.

type ReceiverConfig

type ReceiverConfig struct {
	// SessionID names the session, taken from the first segment received.
	SessionID SessionID

	// FirstReportSerial seeds the report counter. Clause 3.2.2 says the first
	// serial must be chosen randomly for security, and must never be zero.
	// The caller picks it.
	FirstReportSerial uint64

	// MaxBlockSize caps how large a block this session will assemble, in
	// octets. Zero selects DefaultMaxBlockSize.
	//
	// This cap is not in RFC 5326: the protocol puts no ceiling on a block,
	// and a data segment's offset is an SDNV reaching 2^64. Without a limit,
	// one corrupt or hostile segment claiming a huge offset would make the
	// receiver try to allocate that much memory. Set it to what the mission
	// actually sends.
	MaxBlockSize uint64
}

ReceiverConfig describes one incoming LTP session.

type ReceptionClaim

type ReceptionClaim struct {
	Offset uint64
	Length uint64
}

ReceptionClaim is one run of successfully received data, per clause 3.2.2.

The offset is measured from the report's lower bound, NOT from the start of the block. Add the lower bound to get a block offset.

type ReportAckSegment

type ReportAckSegment struct {
	ReportSerial uint64
}

ReportAckSegment is the content of a report-acknowledgment segment, per Clause 3.2.3: just the serial number of the report being acknowledged.

func DecodeReportAckSegment

func DecodeReportAckSegment(data []byte) (*ReportAckSegment, int, error)

DecodeReportAckSegment parses report-ack content.

func (*ReportAckSegment) Encode

func (r *ReportAckSegment) Encode() ([]byte, error)

Encode serializes the report-ack content.

func (*ReportAckSegment) Humanize

func (r *ReportAckSegment) Humanize() string

Humanize returns a human-readable summary.

type ReportSegment

type ReportSegment struct {
	// ReportSerial identifies this report among the receiver's. Never zero.
	ReportSerial uint64
	// CheckpointSerial is the checkpoint that prompted this report, or zero
	// when the report is asynchronous.
	CheckpointSerial uint64
	// UpperBound is the size of the block prefix the claims pertain to.
	UpperBound uint64
	// LowerBound is the size of the interior prefix the claims do NOT pertain
	// to. Claim offsets are relative to it.
	LowerBound uint64
	// Claims are the received runs, in ascending offset order.
	Claims []ReceptionClaim
}

ReportSegment is the content of a report segment, per clause 3.2.2. It tells the sender which parts of the block arrived.

func DecodeReportSegment

func DecodeReportSegment(data []byte) (*ReportSegment, int, error)

DecodeReportSegment parses report segment content.

func (*ReportSegment) ClaimedRanges

func (r *ReportSegment) ClaimedRanges() []ReceptionClaim

ClaimedRanges returns the claims as absolute block offsets, having added the lower bound that clause 3.2.2 measures them from.

func (*ReportSegment) Encode

func (r *ReportSegment) Encode() ([]byte, error)

Encode serializes the report segment content.

func (*ReportSegment) Humanize

func (r *ReportSegment) Humanize() string

Humanize returns a human-readable summary.

func (*ReportSegment) Validate

func (r *ReportSegment) Validate() error

Validate checks the report against the rules of clause 3.2.2.

type Segment

type Segment struct {
	Header *Header

	// Data is set for the data segment types.
	Data *DataSegment
	// Report is set for TypeReport.
	Report *ReportSegment
	// ReportAck is set for TypeReportAck.
	ReportAck *ReportAckSegment
	// Cancel is set for the cancel segment types. Cancel acknowledgments have
	// no content at all, so every field stays nil for those.
	Cancel *CancelSegment
}

Segment is one complete LTP segment: a header, type-specific content, and any trailer extensions.

Exactly one of the content fields is set, chosen by the header's type.

func DecodeSegment

func DecodeSegment(data []byte) (*Segment, error)

DecodeSegment parses one complete LTP segment.

func (*Segment) Encode

func (s *Segment) Encode() ([]byte, error)

Encode serializes the whole segment: header, content, trailer extensions.

func (*Segment) Humanize

func (s *Segment) Humanize() string

Humanize returns a human-readable summary of the whole segment.

func (*Segment) String

func (s *Segment) String() string

String renders a one-line description.

func (*Segment) Validate

func (s *Segment) Validate() error

Validate checks that the content matches the header's type.

type SegmentType

type SegmentType uint8

SegmentType is the 4-bit type code of RFC 5326 clause 3.1.2, built from the CTRL, EXC, Flag 1 and Flag 0 bits.

const (
	// TypeRedData is red data that is neither checkpoint, end of red part,
	// nor end of block.
	TypeRedData SegmentType = 0
	// TypeRedDataCheckpoint is red data that is a checkpoint only.
	TypeRedDataCheckpoint SegmentType = 1
	// TypeRedDataCheckpointEORP is red data that is a checkpoint and the end
	// of the red part, but not the end of the block.
	TypeRedDataCheckpointEORP SegmentType = 2
	// TypeRedDataCheckpointEORPEOB is red data that is a checkpoint, the end
	// of the red part, and the end of the block.
	TypeRedDataCheckpointEORPEOB SegmentType = 3

	// TypeGreenData is green data that is not the end of the block.
	TypeGreenData SegmentType = 4
	// TypeGreenDataEOB is green data that ends the block.
	TypeGreenDataEOB SegmentType = 7

	// TypeReport carries reception claims.
	TypeReport SegmentType = 8
	// TypeReportAck acknowledges a report segment.
	TypeReportAck SegmentType = 9

	// TypeCancelFromSender cancels a session, from the block sender.
	TypeCancelFromSender SegmentType = 12
	// TypeCancelAckToSender acknowledges a cancel from the block sender.
	TypeCancelAckToSender SegmentType = 13
	// TypeCancelFromReceiver cancels a session, from the block receiver.
	TypeCancelFromReceiver SegmentType = 14
	// TypeCancelAckToReceiver acknowledges a cancel from the block receiver.
	TypeCancelAckToReceiver SegmentType = 15
)

Segment type codes, per table clause 3.1.2.

func (SegmentType) Defined

func (t SegmentType) Defined() bool

Defined reports whether the type code is one RFC 5326 clause 3.1.2 assigns a meaning. Codes 5, 6, 10 and 11 are listed as undefined.

func (SegmentType) IsCancel

func (t SegmentType) IsCancel() bool

IsCancel reports whether this is a cancel segment, from either end.

func (SegmentType) IsCancelAck

func (t SegmentType) IsCancelAck() bool

IsCancelAck reports whether this acknowledges a cancel.

func (SegmentType) IsCheckpoint

func (t SegmentType) IsCheckpoint() bool

IsCheckpoint reports whether this data segment is a checkpoint. Clause 3.1.1: any red-part data segment with either low flag set is a checkpoint.

func (SegmentType) IsData

func (t SegmentType) IsData() bool

IsData reports whether this is a data segment, red or green. Clause 3.1.1: the CTRL flag, bit 3, is clear for data.

func (SegmentType) IsEOB

func (t SegmentType) IsEOB() bool

IsEOB reports whether this segment ends the block. Clause 3.1.3: a data segment with both low flags set.

func (SegmentType) IsEORP

func (t SegmentType) IsEORP() bool

IsEORP reports whether this segment ends the red part. Clause 3.1.3: red data with Flag 1 set.

func (SegmentType) IsGreenData

func (t SegmentType) IsGreenData() bool

IsGreenData reports whether this carries green-part data: CTRL clear, EXC set.

func (SegmentType) IsRedData

func (t SegmentType) IsRedData() bool

IsRedData reports whether this carries red-part data. Red data has both the CTRL flag (bit 3) and the EXC flag (bit 2) clear.

func (SegmentType) String

func (t SegmentType) String() string

String names the segment type.

type Sender

type Sender struct {
	// contains filtered or unexported fields
}

Sender drives one outgoing LTP session.

It owns no goroutines and no clock. The caller pumps it: NextSegment returns what to transmit, HandleSegment feeds inbound segments back in, and the caller's own scheduler decides when to retransmit a checkpoint. On a link where a round trip takes an hour, only the mission can pick that timeout.

Usage:

  1. Create with NewSender
  2. Call NextSegment and transmit what it returns, until nothing is pending
  3. Call HandleSegment when a segment arrives on the return link
  4. Check Done or State for completion

A Sender is safe for concurrent use.

func NewSender

func NewSender(block []byte, config SenderConfig) (*Sender, error)

NewSender prepares a session to send one block.

func (*Sender) Cancel

func (s *Sender) Cancel(reason CancelReason) error

Cancel abandons the session. The next segment out is a cancel carrying this reason.

func (*Sender) Done

func (s *Sender) Done() bool

Done reports whether the session has closed or been cancelled.

func (*Sender) HandleSegment

func (s *Sender) HandleSegment(seg *Segment) error

HandleSegment feeds a segment arriving on the return link into the session.

A sender expects reports, cancels from the receiver, and acknowledgments of its own cancels. Anything else is ignored, since a shared link may carry segments for other sessions.

func (*Sender) NextSegment

func (s *Sender) NextSegment() (*Segment, bool, error)

NextSegment returns the next segment to transmit, or ok == false when nothing is pending. A false does not mean the session is finished; check Done for that.

func (*Sender) RedPartAcknowledged

func (s *Sender) RedPartAcknowledged() bool

RedPartAcknowledged reports whether every octet of the red part has been claimed by a report.

func (*Sender) ResendCheckpoint

func (s *Sender) ResendCheckpoint()

ResendCheckpoint re-queues the outstanding red-part gaps. The caller invokes this from its own timer when a report does not come back.

func (*Sender) State

func (s *Sender) State() SessionState

State returns the session state.

type SenderConfig

type SenderConfig struct {
	// SessionID names the session. The engine ID is this sender's.
	SessionID SessionID

	// ClientServiceID names the service at the far end.
	ClientServiceID uint64

	// SegmentSize is the largest client service data payload per segment.
	SegmentSize int

	// RedPartLength is how many leading octets of the block are red, and so
	// delivered reliably. Zero makes the whole block green; a value equal to
	// the block length makes it all red.
	RedPartLength uint64

	// FirstCheckpointSerial seeds the checkpoint counter. Clause 3.2.1 says the
	// first serial must be chosen randomly for security, and must never be
	// zero. The caller picks it, because this package has no randomness
	// policy of its own.
	FirstCheckpointSerial uint64
}

SenderConfig describes one outgoing LTP session.

type SessionID

type SessionID struct {
	EngineID      uint64
	SessionNumber uint64
}

SessionID names a transmission session, per RFC 5326 clause 3.1.

The engine ID identifies the sender, and the session number distinguishes this session from that engine's others. Together they are unique.

func (SessionID) String

func (s SessionID) String() string

String renders the session ID.

type SessionState

type SessionState int

SessionState is where a transmission session has got to.

const (
	// StateActive means the session is transmitting or receiving.
	StateActive SessionState = iota
	// StateWaitingReport means the sender has sent a checkpoint and is
	// waiting for the report that answers it.
	StateWaitingReport
	// StateClosed means the red part is fully acknowledged and the block is
	// complete.
	StateClosed
	// StateCancelled means the session was cancelled at one end or the other.
	StateCancelled
)

func (SessionState) String

func (s SessionState) String() string

String names the state.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL