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