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