-
Notifications
You must be signed in to change notification settings - Fork 79
/
chunk_init_common.go
167 lines (145 loc) · 5.83 KB
/
chunk_init_common.go
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
162
163
164
165
166
167
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
unrecognizedParams []paramHeader
}
const (
initChunkMinLength = 16
initOptionalVarHeaderLength = 4
)
// Init chunk errors
var (
ErrInitChunkParseParamTypeFailed = errors.New("failed to parse param type")
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 {
var pHeader paramHeader
if err := pHeader.unmarshal(raw[offset:]); err != nil {
return fmt.Errorf("%w: %v", ErrInitChunkParseParamTypeFailed, err) //nolint:errorlint
}
p, err := buildParam(pHeader.typ, raw[offset:])
if err != nil {
i.unrecognizedParams = append(i.unrecognizedParams, pHeader)
} else {
i.params = append(i.params, p)
}
padding := getPadding(pHeader.length())
offset += pHeader.length() + padding
remaining -= pHeader.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) //nolint:errorlint
}
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
}