Skip to content
File

Blob: firmware/vendor/sctp-proto/src/chunk/chunk_init.rs

rust286 lines
1use super::{chunk_header::*, chunk_type::*, *};
2use crate::param::param_supported_extensions::ParamSupportedExtensions;
3use crate::param::{param_header::*, *};
4use crate::util::get_padding_size;
5 
6///chunkInitCommon represents an SCTP Chunk body of type INIT and INIT ACK
7///
8/// 0 1 2 3
9/// 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
10///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
11///| Type = 1 | Chunk Flags | Chunk Length |
12///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13///| Initiate Tag |
14///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15///| Advertised Receiver Window Credit (a_rwnd) |
16///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17///| Number of Outbound Streams | Number of Inbound Streams |
18///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19///| Initial TSN |
20///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21///| |
22///| Optional/Variable-Length Parameters |
23///| |
24///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25///
26///The INIT chunk contains the following parameters. Unless otherwise
27///noted, each parameter MUST only be included once in the INIT chunk.
28///
29///Fixed Parameters Status
30///----------------------------------------------
31///Initiate Tag Mandatory
32///Advertised Receiver Window Credit Mandatory
33///Number of Outbound Streams Mandatory
34///Number of Inbound Streams Mandatory
35///Initial TSN Mandatory
36///
37///Init represents an SCTP Chunk of type INIT
38///
39///See chunkInitCommon for the fixed headers
40///
41///Variable Parameters Status Type Value
42///-------------------------------------------------------------
43///IPv4 IP (Note 1) Optional 5
44///IPv6 IP (Note 1) Optional 6
45///Cookie Preservative Optional 9
46///Reserved for ECN Capable (Note 2) Optional 32768 (0x8000)
47///Host Name IP (Note 3) Optional 11
48///Supported IP Types (Note 4) Optional 12
49///
50///
51/// chunkInitAck represents an SCTP Chunk of type INIT ACK
52///
53///See chunkInitCommon for the fixed headers
54///
55///Variable Parameters Status Type Value
56///-------------------------------------------------------------
57///State Cookie Mandatory 7
58///IPv4 IP (Note 1) Optional 5
59///IPv6 IP (Note 1) Optional 6
60///Unrecognized Parameter Optional 8
61///Reserved for ECN Capable (Note 2) Optional 32768 (0x8000)
62///Host Name IP (Note 3) Optional 11<Paste>
63#[derive(Default, Debug, Clone)]
64pub(crate) struct ChunkInit {
65 pub(crate) is_ack: bool,
66 pub(crate) initiate_tag: u32,
67 pub(crate) advertised_receiver_window_credit: u32,
68 pub(crate) num_outbound_streams: u16,
69 pub(crate) num_inbound_streams: u16,
70 pub(crate) initial_tsn: u32,
71 pub(crate) params: Vec<Box<dyn Param + Send + Sync>>,
72}
73 
74pub(crate) type ChunkInitAck = ChunkInit;
75 
76pub(crate) const INIT_CHUNK_MIN_LENGTH: usize = 16;
77pub(crate) const INIT_OPTIONAL_VAR_HEADER_LENGTH: usize = 4;
78 
79/// makes chunkInitCommon printable
80impl fmt::Display for ChunkInit {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 let mut res = format!(
83 "is_ack: {}
84 initiate_tag: {}
85 advertised_receiver_window_credit: {}
86 num_outbound_streams: {}
87 num_inbound_streams: {}
88 initial_tsn: {}",
89 self.is_ack,
90 self.initiate_tag,
91 self.advertised_receiver_window_credit,
92 self.num_outbound_streams,
93 self.num_inbound_streams,
94 self.initial_tsn,
95 );
96 
97 for (i, param) in self.params.iter().enumerate() {
98 res += format!("Param {}:\n {}", i, param).as_str();
99 }
100 write!(f, "{} {}", self.header(), res)
101 }
102}
103 
104impl Chunk for ChunkInit {
105 fn header(&self) -> ChunkHeader {
106 ChunkHeader {
107 typ: if self.is_ack { CT_INIT_ACK } else { CT_INIT },
108 flags: 0,
109 value_length: self.value_length() as u16,
110 }
111 }
112 
113 ///https://tools.ietf.org/html/rfc4960#section-3.2.1
114 ///
115 ///Chunk values of SCTP control chunks consist of a chunk-type-specific
116 ///header of required fields, followed by zero or more parameters. The
117 ///optional and variable-length parameters contained in a chunk are
118 ///defined in a Type-Length-Value format as shown below.
119 ///
120 ///0 1 2 3
121 ///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
122 ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
123 ///| Parameter Type | Parameter Length |
124 ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
125 ///| |
126 ///| Parameter Value |
127 ///| |
128 ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
129 fn unmarshal(raw: &Bytes) -> Result<Self> {
130 let header = ChunkHeader::unmarshal(raw)?;
131 
132 if !(header.typ == CT_INIT || header.typ == CT_INIT_ACK) {
133 return Err(Error::ErrChunkTypeNotTypeInit);
134 } else if header.value_length() < INIT_CHUNK_MIN_LENGTH {
135 return Err(Error::ErrChunkValueNotLongEnough);
136 }
137 
138 // The Chunk Flags field in INIT is reserved, and all bits in it should
139 // be set to 0 by the sender and ignored by the receiver. The sequence
140 // of parameters within an INIT can be processed in any order.
141 if header.flags != 0 {
142 return Err(Error::ErrChunkTypeInitFlagZero);
143 }
144 
145 let reader = &mut raw.slice(CHUNK_HEADER_SIZE..CHUNK_HEADER_SIZE + header.value_length());
146 
147 let initiate_tag = reader.get_u32();
148 let advertised_receiver_window_credit = reader.get_u32();
149 let num_outbound_streams = reader.get_u16();
150 let num_inbound_streams = reader.get_u16();
151 let initial_tsn = reader.get_u32();
152 
153 let mut params = vec![];
154 let mut offset = CHUNK_HEADER_SIZE + INIT_CHUNK_MIN_LENGTH;
155 let value_end = CHUNK_HEADER_SIZE + header.value_length();
156 let mut remaining = value_end as isize - offset as isize;
157 while remaining > INIT_OPTIONAL_VAR_HEADER_LENGTH as isize {
158 let p = build_param(&raw.slice(offset..CHUNK_HEADER_SIZE + header.value_length()))?;
159 let p_len = PARAM_HEADER_LENGTH + p.value_length();
160 let len_plus_padding = p_len + get_padding_size(p_len);
161 params.push(p);
162 offset += len_plus_padding;
163 remaining -= len_plus_padding as isize;
164 }
165 
166 Ok(ChunkInit {
167 is_ack: header.typ == CT_INIT_ACK,
168 initiate_tag,
169 advertised_receiver_window_credit,
170 num_outbound_streams,
171 num_inbound_streams,
172 initial_tsn,
173 params,
174 })
175 }
176 
177 fn marshal_to(&self, writer: &mut BytesMut) -> Result<usize> {
178 self.header().marshal_to(writer)?;
179 
180 writer.put_u32(self.initiate_tag);
181 writer.put_u32(self.advertised_receiver_window_credit);
182 writer.put_u16(self.num_outbound_streams);
183 writer.put_u16(self.num_inbound_streams);
184 writer.put_u32(self.initial_tsn);
185 for (idx, p) in self.params.iter().enumerate() {
186 let pp = p.marshal()?;
187 let pp_len = pp.len();
188 writer.extend(pp);
189 
190 // Chunks (including Type, Length, and Value fields) are padded out
191 // by the sender with all zero bytes to be a multiple of 4 bytes
192 // long. This padding MUST NOT be more than 3 bytes in total. The
193 // Chunk Length value does not include terminating padding of the
194 // chunk. *However, it does include padding of any variable-length
195 // parameter except the last parameter in the chunk.* The receiver
196 // MUST ignore the padding.
197 if idx != self.params.len() - 1 {
198 let cnt = get_padding_size(pp_len);
199 writer.extend(vec![0u8; cnt]);
200 }
201 }
202 
203 Ok(writer.len())
204 }
205 
206 fn check(&self) -> Result<()> {
207 // The receiver of the INIT (the responding end) records the value of
208 // the Initiate Tag parameter. This value MUST be placed into the
209 // Verification Tag field of every SCTP packet that the receiver of
210 // the INIT transmits within this association.
211 //
212 // The Initiate Tag is allowed to have any value except 0. See
213 // Section 5.3.1 for more on the selection of the tag value.
214 //
215 // If the value of the Initiate Tag in a received INIT chunk is found
216 // to be 0, the receiver MUST treat it as an error and close the
217 // association by transmitting an ABORT.
218 if self.initiate_tag == 0 {
219 return Err(Error::ErrChunkTypeInitInitiateTagZero);
220 }
221 
222 // Defines the maximum number of streams the sender of this INIT
223 // chunk allows the peer end to create in this association. The
224 // value 0 MUST NOT be used.
225 //
226 // Note: There is no negotiation of the actual number of streams but
227 // instead the two endpoints will use the min(requested, offered).
228 // See Section 5.1.1 for details.
229 //
230 // Note: A receiver of an INIT with the MIS value of 0 SHOULD abort
231 // the association.
232 if self.num_inbound_streams == 0 {
233 return Err(Error::ErrInitInboundStreamRequestZero);
234 }
235 
236 // Defines the number of outbound streams the sender of this INIT
237 // chunk wishes to create in this association. The value of 0 MUST
238 // NOT be used.
239 //
240 // Note: A receiver of an INIT with the OS value set to 0 SHOULD
241 // abort the association.
242 
243 if self.num_outbound_streams == 0 {
244 return Err(Error::ErrInitOutboundStreamRequestZero);
245 }
246 
247 // An SCTP receiver MUST be able to receive a minimum of 1500 bytes in
248 // one SCTP packet. This means that an SCTP endpoint MUST NOT indicate
249 // less than 1500 bytes in its initial a_rwnd sent in the INIT or INIT
250 // ACK.
251 if self.advertised_receiver_window_credit < 1500 {
252 return Err(Error::ErrInitAdvertisedReceiver1500);
253 }
254 
255 Ok(())
256 }
257 
258 fn value_length(&self) -> usize {
259 let mut l = 4 + 4 + 2 + 2 + 4;
260 for (idx, p) in self.params.iter().enumerate() {
261 let p_len = PARAM_HEADER_LENGTH + p.value_length();
262 l += p_len;
263 if idx != self.params.len() - 1 {
264 l += get_padding_size(p_len);
265 }
266 }
267 l
268 }
269 
270 fn as_any(&self) -> &(dyn Any + Send + Sync) {
271 self
272 }
273}
274 
275impl ChunkInit {
276 pub(crate) fn set_supported_extensions(&mut self) {
277 // RFC5061 https://tools.ietf.org/html/rfc6525#section-5.2
278 // An implementation supporting this (Supported Extensions Parameter)
279 // extension MUST list the ASCONF, the ASCONF-ACK, and the AUTH chunks
280 // in its INIT and INIT-ACK parameters.
281 self.params.push(Box::new(ParamSupportedExtensions {
282 chunk_types: vec![CT_RECONFIG, CT_FORWARD_TSN],
283 }));
284 }
285}