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
- Variables
- type CancelReason
- type CancelSegment
- type DataSegment
- type Extension
- type Header
- type Receiver
- func (r *Receiver) Block() []byte
- func (r *Receiver) Cancel(reason CancelReason) error
- func (r *Receiver) Complete() bool
- func (r *Receiver) Done() bool
- func (r *Receiver) HandleSegment(seg *Segment) error
- func (r *Receiver) MissingRanges() []ReceptionClaim
- func (r *Receiver) NextSegment() (*Segment, bool, error)
- func (r *Receiver) RedPart() []byte
- func (r *Receiver) RedPartComplete() bool
- func (r *Receiver) RequestReport()
- func (r *Receiver) State() SessionState
- type ReceiverConfig
- type ReceptionClaim
- type ReportAckSegment
- type ReportSegment
- type Segment
- type SegmentType
- func (t SegmentType) Defined() bool
- func (t SegmentType) IsCancel() bool
- func (t SegmentType) IsCancelAck() bool
- func (t SegmentType) IsCheckpoint() bool
- func (t SegmentType) IsData() bool
- func (t SegmentType) IsEOB() bool
- func (t SegmentType) IsEORP() bool
- func (t SegmentType) IsGreenData() bool
- func (t SegmentType) IsRedData() bool
- func (t SegmentType) String() string
- type Sender
- func (s *Sender) Cancel(reason CancelReason) error
- func (s *Sender) Done() bool
- func (s *Sender) HandleSegment(seg *Segment) error
- func (s *Sender) NextSegment() (*Segment, bool, error)
- func (s *Sender) RedPartAcknowledged() bool
- func (s *Sender) ResendCheckpoint()
- func (s *Sender) State() SessionState
- type SenderConfig
- type SessionID
- type SessionState
Constants ¶
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).
const DefaultMaxBlockSize = 64 << 20
DefaultMaxBlockSize bounds a received block when ReceiverConfig leaves MaxBlockSize at zero: 64 MiB.
const DefaultSegmentSize = 1024
DefaultSegmentSize is the payload size used when SenderConfig leaves SegmentSize at zero.
const Version = 0
Version is the LTP segment version number, per RFC 5326 clause 3.1. Only 0 is defined.
Variables ¶
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) 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 ¶
Extension is one header or trailer extension TLV, per clause 3.1.4: a one-octet tag, an SDNV length, then the value.
type Header ¶
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 ¶
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 ¶
Encode serializes the header: control octet, session ID, extension counts, then the header extensions. Trailer extensions are appended by Segment.Encode after the content.
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 ¶
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) HandleSegment ¶
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 ¶
NextSegment returns the next segment to send back, or ok == false when nothing is pending.
func (*Receiver) RedPartComplete ¶
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.
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 ¶
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 ¶
DecodeSegment parses one complete LTP segment.
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.
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:
- Create with NewSender
- Call NextSegment and transmit what it returns, until nothing is pending
- Call HandleSegment when a segment arrives on the return link
- 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) HandleSegment ¶
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 ¶
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 ¶
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.
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 ¶
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.
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 )