package sctp
import (
"encoding/binary"
"errors"
"fmt"
)
type gapAckBlock struct {
start uint16
end uint16
}
var (
ErrChunkTypeNotSack = errors .New ("ChunkType is not of type SACK" )
ErrSackSizeNotLargeEnoughInfo = errors .New ("SACK Chunk size is not large enough to contain header" )
ErrSackSizeNotMatchPredicted = errors .New ("SACK Chunk size does not match predicted amount from header values" )
)
func (g gapAckBlock ) String () string {
return fmt .Sprintf ("%d - %d" , g .start , g .end )
}
type chunkSelectiveAck struct {
chunkHeader
cumulativeTSNAck uint32
advertisedReceiverWindowCredit uint32
gapAckBlocks []gapAckBlock
duplicateTSN []uint32
}
const (
selectiveAckHeaderSize = 12
)
func (s *chunkSelectiveAck ) unmarshal (raw []byte ) error {
if err := s .chunkHeader .unmarshal (raw ); err != nil {
return err
}
if s .typ != ctSack {
return fmt .Errorf ("%w: actually is %s" , ErrChunkTypeNotSack , s .typ .String ())
}
if len (s .raw ) < selectiveAckHeaderSize {
return fmt .Errorf ("%w: %v remaining, needs %v bytes" , ErrSackSizeNotLargeEnoughInfo ,
len (s .raw ), selectiveAckHeaderSize )
}
s .cumulativeTSNAck = binary .BigEndian .Uint32 (s .raw [0 :])
s .advertisedReceiverWindowCredit = binary .BigEndian .Uint32 (s .raw [4 :])
s .gapAckBlocks = make ([]gapAckBlock , binary .BigEndian .Uint16 (s .raw [8 :]))
s .duplicateTSN = make ([]uint32 , binary .BigEndian .Uint16 (s .raw [10 :]))
if len (s .raw ) != selectiveAckHeaderSize +(4 *len (s .gapAckBlocks )+(4 *len (s .duplicateTSN ))) {
return ErrSackSizeNotMatchPredicted
}
offset := selectiveAckHeaderSize
for i := range s .gapAckBlocks {
s .gapAckBlocks [i ].start = binary .BigEndian .Uint16 (s .raw [offset :])
s .gapAckBlocks [i ].end = binary .BigEndian .Uint16 (s .raw [offset +2 :])
offset += 4
}
for i := range s .duplicateTSN {
s .duplicateTSN [i ] = binary .BigEndian .Uint32 (s .raw [offset :])
offset += 4
}
return nil
}
func (s *chunkSelectiveAck ) marshal () ([]byte , error ) {
sackRaw := make ([]byte , selectiveAckHeaderSize +(4 *len (s .gapAckBlocks )+(4 *len (s .duplicateTSN ))))
binary .BigEndian .PutUint32 (sackRaw [0 :], s .cumulativeTSNAck )
binary .BigEndian .PutUint32 (sackRaw [4 :], s .advertisedReceiverWindowCredit )
binary .BigEndian .PutUint16 (sackRaw [8 :], uint16 (len (s .gapAckBlocks )))
binary .BigEndian .PutUint16 (sackRaw [10 :], uint16 (len (s .duplicateTSN )))
offset := selectiveAckHeaderSize
for _ , g := range s .gapAckBlocks {
binary .BigEndian .PutUint16 (sackRaw [offset :], g .start )
binary .BigEndian .PutUint16 (sackRaw [offset +2 :], g .end )
offset += 4
}
for _ , t := range s .duplicateTSN {
binary .BigEndian .PutUint32 (sackRaw [offset :], t )
offset += 4
}
s .chunkHeader .typ = ctSack
s .chunkHeader .raw = sackRaw
return s .chunkHeader .marshal ()
}
func (s *chunkSelectiveAck ) check () (abort bool , err error ) {
return false , nil
}
func (s *chunkSelectiveAck ) String () string {
res := fmt .Sprintf ("SACK cumTsnAck=%d arwnd=%d dupTsn=%d" ,
s .cumulativeTSNAck ,
s .advertisedReceiverWindowCredit ,
s .duplicateTSN )
for _ , gap := range s .gapAckBlocks {
res = fmt .Sprintf ("%s\n gap ack: %s" , res , gap )
}
return res
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .