File
Blob: firmware/vendor/sctp-proto/src/chunk/chunk_init.rs
| 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 | ///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)] |
| 64 | pub(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 | |
| 74 | pub(crate) type ChunkInitAck = ChunkInit; |
| 75 | |
| 76 | pub(crate) const INIT_CHUNK_MIN_LENGTH: usize = 16; |
| 77 | pub(crate) const INIT_OPTIONAL_VAR_HEADER_LENGTH: usize = 4; |
| 78 | |
| 79 | /// makes chunkInitCommon printable |
| 80 | impl 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 | |
| 104 | impl 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 | |
| 275 | impl 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 | } |