-
Notifications
You must be signed in to change notification settings - Fork 79
/
chunk_init.go
142 lines (121 loc) · 4.95 KB
/
chunk_init.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
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package sctp // nolint:dupl
import (
"errors"
"fmt"
)
/*
Init represents an SCTP Chunk of type INIT
See chunkInitCommon for the fixed headers
Variable Parameters Status Type Value
-------------------------------------------------------------
IPv4 IP (Note 1) Optional 5
IPv6 IP (Note 1) Optional 6
Cookie Preservative Optional 9
Reserved for ECN Capable (Note 2) Optional 32768 (0x8000)
Host Name IP (Note 3) Optional 11
Supported IP Types (Note 4) Optional 12
*/
type chunkInit struct {
chunkHeader
chunkInitCommon
}
// Init chunk errors
var (
ErrChunkTypeNotTypeInit = errors.New("ChunkType is not of type INIT")
ErrChunkValueNotLongEnough = errors.New("chunk Value isn't long enough for mandatory parameters exp")
ErrChunkTypeInitFlagZero = errors.New("ChunkType of type INIT flags must be all 0")
ErrChunkTypeInitUnmarshalFailed = errors.New("failed to unmarshal INIT body")
ErrChunkTypeInitMarshalFailed = errors.New("failed marshaling INIT common data")
ErrChunkTypeInitInitateTagZero = errors.New("ChunkType of type INIT ACK InitiateTag must not be 0")
ErrInitInboundStreamRequestZero = errors.New("INIT ACK inbound stream request must be > 0")
ErrInitOutboundStreamRequestZero = errors.New("INIT ACK outbound stream request must be > 0")
ErrInitAdvertisedReceiver1500 = errors.New("INIT ACK Advertised Receiver Window Credit (a_rwnd) must be >= 1500")
ErrInitUnknownParam = errors.New("INIT with unknown param")
)
func (i *chunkInit) unmarshal(raw []byte) error {
if err := i.chunkHeader.unmarshal(raw); err != nil {
return err
}
if i.typ != ctInit {
return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotTypeInit, i.typ.String())
} else if len(i.raw) < initChunkMinLength {
return fmt.Errorf("%w: %d actual: %d", ErrChunkValueNotLongEnough, initChunkMinLength, len(i.raw))
}
// The Chunk Flags field in INIT is reserved, and all bits in it should
// be set to 0 by the sender and ignored by the receiver. The sequence
// of parameters within an INIT can be processed in any order.
if i.flags != 0 {
return ErrChunkTypeInitFlagZero
}
if err := i.chunkInitCommon.unmarshal(i.raw); err != nil {
return fmt.Errorf("%w: %v", ErrChunkTypeInitUnmarshalFailed, err) //nolint:errorlint
}
return nil
}
func (i *chunkInit) marshal() ([]byte, error) {
initShared, err := i.chunkInitCommon.marshal()
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrChunkTypeInitMarshalFailed, err) //nolint:errorlint
}
i.chunkHeader.typ = ctInit
i.chunkHeader.raw = initShared
return i.chunkHeader.marshal()
}
func (i *chunkInit) check() (abort bool, err error) {
// The receiver of the INIT (the responding end) records the value of
// the Initiate Tag parameter. This value MUST be placed into the
// Verification Tag field of every SCTP packet that the receiver of
// the INIT transmits within this association.
//
// The Initiate Tag is allowed to have any value except 0. See
// Section 5.3.1 for more on the selection of the tag value.
//
// If the value of the Initiate Tag in a received INIT chunk is found
// to be 0, the receiver MUST treat it as an error and close the
// association by transmitting an ABORT.
if i.initiateTag == 0 {
return true, ErrChunkTypeInitInitateTagZero
}
// Defines the maximum number of streams the sender of this INIT
// chunk allows the peer end to create in this association. The
// value 0 MUST NOT be used.
//
// Note: There is no negotiation of the actual number of streams but
// instead the two endpoints will use the min(requested, offered).
// See Section 5.1.1 for details.
//
// Note: A receiver of an INIT with the MIS value of 0 SHOULD abort
// the association.
if i.numInboundStreams == 0 {
return true, ErrInitInboundStreamRequestZero
}
// Defines the number of outbound streams the sender of this INIT
// chunk wishes to create in this association. The value of 0 MUST
// NOT be used.
//
// Note: A receiver of an INIT with the OS value set to 0 SHOULD
// abort the association.
if i.numOutboundStreams == 0 {
return true, ErrInitOutboundStreamRequestZero
}
// An SCTP receiver MUST be able to receive a minimum of 1500 bytes in
// one SCTP packet. This means that an SCTP endpoint MUST NOT indicate
// less than 1500 bytes in its initial a_rwnd sent in the INIT or INIT
// ACK.
if i.advertisedReceiverWindowCredit < 1500 {
return true, ErrInitAdvertisedReceiver1500
}
for _, p := range i.unrecognizedParams {
if p.unrecognizedAction == paramHeaderUnrecognizedActionStop ||
p.unrecognizedAction == paramHeaderUnrecognizedActionStopAndReport {
return true, ErrInitUnknownParam
}
}
return false, nil
}
// String makes chunkInit printable
func (i *chunkInit) String() string {
return fmt.Sprintf("%s\n%s", i.chunkHeader, i.chunkInitCommon)
}