1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
package sctp
import (
"encoding/binary"
"errors"
"fmt"
)
/*
chunkInitCommon represents an SCTP Chunk body of type INIT and INIT ACK
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 = 1 | Chunk Flags | Chunk Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Initiate Tag |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Advertised Receiver Window Credit (a_rwnd) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Number of Outbound Streams | Number of Inbound Streams |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Initial TSN |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Optional/Variable-Length Parameters |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
The INIT chunk contains the following parameters. Unless otherwise
noted, each parameter MUST only be included once in the INIT chunk.
Fixed Parameters Status
----------------------------------------------
Initiate Tag Mandatory
Advertised Receiver Window Credit Mandatory
Number of Outbound Streams Mandatory
Number of Inbound Streams Mandatory
Initial TSN Mandatory
*/
type chunkInitCommon struct {
initiateTag uint32
advertisedReceiverWindowCredit uint32
numOutboundStreams uint16
numInboundStreams uint16
initialTSN uint32
params []param
}
const (
initChunkMinLength = 16
initOptionalVarHeaderLength = 4
)
// Init chunk errors
var (
ErrInitChunkParseParamTypeFailed = errors.New("failed to parse param type")
ErrInitChunkUnmarshalParam = errors.New("failed unmarshalling param in Init Chunk")
ErrInitAckMarshalParam = errors.New("unable to marshal parameter for INIT/INITACK")
)
func (i *chunkInitCommon) unmarshal(raw []byte) error {
i.initiateTag = binary.BigEndian.Uint32(raw[0:])
i.advertisedReceiverWindowCredit = binary.BigEndian.Uint32(raw[4:])
i.numOutboundStreams = binary.BigEndian.Uint16(raw[8:])
i.numInboundStreams = binary.BigEndian.Uint16(raw[10:])
i.initialTSN = binary.BigEndian.Uint32(raw[12:])
// https://tools.ietf.org/html/rfc4960#section-3.2.1
//
// Chunk values of SCTP control chunks consist of a chunk-type-specific
// header of required fields, followed by zero or more parameters. The
// optional and variable-length parameters contained in a chunk are
// defined in a Type-Length-Value format as shown below.
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Parameter Type | Parameter Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// | Parameter Value |
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
offset := initChunkMinLength
remaining := len(raw) - offset
for remaining > 0 {
if remaining > initOptionalVarHeaderLength {
pType, err := parseParamType(raw[offset:])
if err != nil {
return fmt.Errorf("%w: %v", ErrInitChunkParseParamTypeFailed, err)
}
p, err := buildParam(pType, raw[offset:])
if err != nil {
return fmt.Errorf("%w: %v", ErrInitChunkUnmarshalParam, err)
}
i.params = append(i.params, p)
padding := getPadding(p.length())
offset += p.length() + padding
remaining -= p.length() + padding
} else {
break
}
}
return nil
}
func (i *chunkInitCommon) marshal() ([]byte, error) {
out := make([]byte, initChunkMinLength)
binary.BigEndian.PutUint32(out[0:], i.initiateTag)
binary.BigEndian.PutUint32(out[4:], i.advertisedReceiverWindowCredit)
binary.BigEndian.PutUint16(out[8:], i.numOutboundStreams)
binary.BigEndian.PutUint16(out[10:], i.numInboundStreams)
binary.BigEndian.PutUint32(out[12:], i.initialTSN)
for idx, p := range i.params {
pp, err := p.marshal()
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrInitAckMarshalParam, err)
}
out = append(out, pp...)
// Chunks (including Type, Length, and Value fields) are padded out
// by the sender with all zero bytes to be a multiple of 4 bytes
// long. This padding MUST NOT be more than 3 bytes in total. The
// Chunk Length value does not include terminating padding of the
// chunk. *However, it does include padding of any variable-length
// parameter except the last parameter in the chunk.* The receiver
// MUST ignore the padding.
if idx != len(i.params)-1 {
out = padByte(out, getPadding(len(pp)))
}
}
return out, nil
}
// String makes chunkInitCommon printable
func (i chunkInitCommon) String() string {
format := `initiateTag: %d
advertisedReceiverWindowCredit: %d
numOutboundStreams: %d
numInboundStreams: %d
initialTSN: %d`
res := fmt.Sprintf(format,
i.initiateTag,
i.advertisedReceiverWindowCredit,
i.numOutboundStreams,
i.numInboundStreams,
i.initialTSN,
)
for i, param := range i.params {
res += fmt.Sprintf("Param %d:\n %s", i, param)
}
return res
}
|