152 lines
4.5 KiB
Go
152 lines
4.5 KiB
Go
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package sctp
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// This chunk shall be used by the data sender to inform the data
|
|
// receiver to adjust its cumulative received TSN point forward because
|
|
// some missing TSNs are associated with data chunks that SHOULD NOT be
|
|
// transmitted or retransmitted by the sender.
|
|
//
|
|
// 0 1 2 3
|
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | Type = 192 | Flags = 0x00 | Length = Variable |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | New Cumulative TSN |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | Stream-1 | Stream Sequence-1 |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// \ /
|
|
// / \
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | Stream-N | Stream Sequence-N |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
type chunkForwardTSN struct {
|
|
chunkHeader
|
|
|
|
// This indicates the new cumulative TSN to the data receiver. Upon
|
|
// the reception of this value, the data receiver MUST consider
|
|
// any missing TSNs earlier than or equal to this value as received,
|
|
// and stop reporting them as gaps in any subsequent SACKs.
|
|
newCumulativeTSN uint32
|
|
|
|
streams []chunkForwardTSNStream
|
|
}
|
|
|
|
const (
|
|
newCumulativeTSNLength = 4
|
|
forwardTSNStreamLength = 4
|
|
)
|
|
|
|
// Forward TSN chunk errors
|
|
var (
|
|
ErrMarshalStreamFailed = errors.New("failed to marshal stream")
|
|
ErrChunkTooShort = errors.New("chunk too short")
|
|
)
|
|
|
|
func (c *chunkForwardTSN) unmarshal(raw []byte) error {
|
|
if err := c.chunkHeader.unmarshal(raw); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(c.raw) < newCumulativeTSNLength {
|
|
return ErrChunkTooShort
|
|
}
|
|
|
|
c.newCumulativeTSN = binary.BigEndian.Uint32(c.raw[0:])
|
|
|
|
offset := newCumulativeTSNLength
|
|
remaining := len(c.raw) - offset
|
|
for remaining > 0 {
|
|
s := chunkForwardTSNStream{}
|
|
|
|
if err := s.unmarshal(c.raw[offset:]); err != nil {
|
|
return fmt.Errorf("%w: %v", ErrMarshalStreamFailed, err) //nolint:errorlint
|
|
}
|
|
|
|
c.streams = append(c.streams, s)
|
|
|
|
offset += s.length()
|
|
remaining -= s.length()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *chunkForwardTSN) marshal() ([]byte, error) {
|
|
out := make([]byte, newCumulativeTSNLength)
|
|
binary.BigEndian.PutUint32(out[0:], c.newCumulativeTSN)
|
|
|
|
for _, s := range c.streams {
|
|
b, err := s.marshal()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: %v", ErrMarshalStreamFailed, err) //nolint:errorlint
|
|
}
|
|
out = append(out, b...)
|
|
}
|
|
|
|
c.typ = ctForwardTSN
|
|
c.raw = out
|
|
return c.chunkHeader.marshal()
|
|
}
|
|
|
|
func (c *chunkForwardTSN) check() (abort bool, err error) {
|
|
return true, nil
|
|
}
|
|
|
|
// String makes chunkForwardTSN printable
|
|
func (c *chunkForwardTSN) String() string {
|
|
res := fmt.Sprintf("New Cumulative TSN: %d\n", c.newCumulativeTSN)
|
|
for _, s := range c.streams {
|
|
res += fmt.Sprintf(" - si=%d, ssn=%d\n", s.identifier, s.sequence)
|
|
}
|
|
return res
|
|
}
|
|
|
|
type chunkForwardTSNStream struct {
|
|
// This field holds a stream number that was skipped by this
|
|
// FWD-TSN.
|
|
identifier uint16
|
|
|
|
// This field holds the sequence number associated with the stream
|
|
// that was skipped. The stream sequence field holds the largest
|
|
// stream sequence number in this stream being skipped. The receiver
|
|
// of the FWD-TSN's can use the Stream-N and Stream Sequence-N fields
|
|
// to enable delivery of any stranded TSN's that remain on the stream
|
|
// re-ordering queues. This field MUST NOT report TSN's corresponding
|
|
// to DATA chunks that are marked as unordered. For ordered DATA
|
|
// chunks this field MUST be filled in.
|
|
sequence uint16
|
|
}
|
|
|
|
func (s *chunkForwardTSNStream) length() int {
|
|
return forwardTSNStreamLength
|
|
}
|
|
|
|
func (s *chunkForwardTSNStream) unmarshal(raw []byte) error {
|
|
if len(raw) < forwardTSNStreamLength {
|
|
return ErrChunkTooShort
|
|
}
|
|
s.identifier = binary.BigEndian.Uint16(raw[0:])
|
|
s.sequence = binary.BigEndian.Uint16(raw[2:])
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *chunkForwardTSNStream) marshal() ([]byte, error) { // nolint:unparam
|
|
out := make([]byte, forwardTSNStreamLength)
|
|
|
|
binary.BigEndian.PutUint16(out[0:], s.identifier)
|
|
binary.BigEndian.PutUint16(out[2:], s.sequence)
|
|
|
|
return out, nil
|
|
}
|