Skip to content
File

Blob: firmware/vendor/sctp-proto/src/packet.rs

rust506 lines
1use crate::chunk::Chunk;
2use crate::chunk::chunk_abort::ChunkAbort;
3use crate::chunk::chunk_cookie_ack::ChunkCookieAck;
4use crate::chunk::chunk_cookie_echo::ChunkCookieEcho;
5use crate::chunk::chunk_error::ChunkError;
6use crate::chunk::chunk_forward_tsn::ChunkForwardTsn;
7use crate::chunk::chunk_header::*;
8use crate::chunk::chunk_heartbeat::ChunkHeartbeat;
9use crate::chunk::chunk_i_forward_tsn::ChunkIForwardTsn;
10use crate::chunk::chunk_init::ChunkInit;
11use crate::chunk::chunk_payload_data::ChunkPayloadData;
12use crate::chunk::chunk_reconfig::ChunkReconfig;
13use crate::chunk::chunk_selective_ack::ChunkSelectiveAck;
14use crate::chunk::chunk_shutdown::ChunkShutdown;
15use crate::chunk::chunk_shutdown_ack::ChunkShutdownAck;
16use crate::chunk::chunk_shutdown_complete::ChunkShutdownComplete;
17use crate::chunk::chunk_type::*;
18use crate::error::{Error, Result};
19use crate::util::*;
20 
21use alloc::boxed::Box;
22use alloc::vec;
23use alloc::vec::Vec;
24use bytes::{Buf, BufMut, Bytes, BytesMut};
25use core::fmt;
26 
27///Packet represents an SCTP packet, defined in https://tools.ietf.org/html/rfc4960#section-3
28///An SCTP packet is composed of a common header and chunks. A chunk
29///contains either control information or user data.
30///
31///
32///SCTP Packet Format
33/// 0 1 2 3
34/// 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
35///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36///| Common Header |
37///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38///| Chunk #1 |
39///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
40///| ... |
41///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
42///| Chunk #n |
43///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44///
45///
46///SCTP Common Header Format
47///
48/// 0 1 2 3
49/// 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
50///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
51///| Source Value Number | Destination Value Number |
52///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
53///| Verification Tag |
54///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55///| Checksum |
56///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
57pub(crate) const PACKET_HEADER_SIZE: usize = 12;
58const INITIATE_TAG_LENGTH: usize = 4;
59 
60#[derive(Default, Debug)]
61pub(crate) struct CommonHeader {
62 pub(crate) source_port: u16,
63 pub(crate) destination_port: u16,
64 pub(crate) verification_tag: u32,
65}
66 
67#[derive(Default, Debug)]
68pub struct PartialDecode {
69 pub(crate) common_header: CommonHeader,
70 pub(crate) remaining: Bytes,
71 pub(crate) first_chunk_type: ChunkType,
72 pub(crate) initiate_tag: Option<u32>,
73 pub(crate) cookie: Option<Bytes>,
74}
75 
76impl PartialDecode {
77 pub(crate) fn unmarshal(raw: &Bytes) -> Result<Self> {
78 if raw.len() < PACKET_HEADER_SIZE {
79 return Err(Error::ErrPacketRawTooSmall);
80 }
81 
82 let reader = &mut raw.clone();
83 
84 let source_port = reader.get_u16();
85 let destination_port = reader.get_u16();
86 let verification_tag = reader.get_u32();
87 let their_checksum = reader.get_u32_le();
88 let our_checksum = generate_packet_checksum(raw);
89 
90 if their_checksum != our_checksum {
91 return Err(Error::ErrChecksumMismatch);
92 }
93 
94 if reader.remaining() < CHUNK_HEADER_SIZE {
95 return Err(Error::ErrParseSctpChunkNotEnoughData);
96 }
97 
98 let header = ChunkHeader::unmarshal(reader)?;
99 reader.advance(CHUNK_HEADER_SIZE);
100 
101 let mut initiate_tag = None;
102 let mut cookie = None;
103 match header.typ {
104 CT_INIT | CT_INIT_ACK => {
105 if header.value_length() < INITIATE_TAG_LENGTH {
106 return Err(Error::ErrChunkValueNotLongEnough);
107 }
108 initiate_tag = Some(reader.get_u32());
109 }
110 CT_COOKIE_ECHO => {
111 let end = PACKET_HEADER_SIZE + CHUNK_HEADER_SIZE + header.value_length();
112 if end > raw.len() {
113 return Err(Error::ErrChunkValueNotLongEnough);
114 }
115 cookie = Some(raw.slice(PACKET_HEADER_SIZE + CHUNK_HEADER_SIZE..end));
116 }
117 _ => {}
118 }
119 
120 Ok(PartialDecode {
121 common_header: CommonHeader {
122 source_port,
123 destination_port,
124 verification_tag,
125 },
126 remaining: raw.slice(PACKET_HEADER_SIZE..),
127 first_chunk_type: header.typ,
128 initiate_tag,
129 cookie,
130 })
131 }
132 
133 pub(crate) fn finish(self) -> Result<Packet> {
134 let mut chunks = vec![];
135 let mut offset = 0;
136 loop {
137 // Exact match, no more chunks
138 if offset == self.remaining.len() {
139 break;
140 } else if offset + CHUNK_HEADER_SIZE > self.remaining.len() {
141 return Err(Error::ErrParseSctpChunkNotEnoughData);
142 }
143 
144 let ct = ChunkType(self.remaining[offset]);
145 let c: Box<dyn Chunk + Send + Sync> = match ct {
146 CT_INIT => Box::new(ChunkInit::unmarshal(&self.remaining.slice(offset..))?),
147 CT_INIT_ACK => Box::new(ChunkInit::unmarshal(&self.remaining.slice(offset..))?),
148 CT_ABORT => Box::new(ChunkAbort::unmarshal(&self.remaining.slice(offset..))?),
149 CT_COOKIE_ECHO => {
150 Box::new(ChunkCookieEcho::unmarshal(&self.remaining.slice(offset..))?)
151 }
152 CT_COOKIE_ACK => {
153 Box::new(ChunkCookieAck::unmarshal(&self.remaining.slice(offset..))?)
154 }
155 CT_HEARTBEAT => {
156 Box::new(ChunkHeartbeat::unmarshal(&self.remaining.slice(offset..))?)
157 }
158 CT_PAYLOAD_DATA => Box::new(ChunkPayloadData::unmarshal(
159 &self.remaining.slice(offset..),
160 )?),
161 CT_SACK => Box::new(ChunkSelectiveAck::unmarshal(
162 &self.remaining.slice(offset..),
163 )?),
164 CT_RECONFIG => Box::new(ChunkReconfig::unmarshal(&self.remaining.slice(offset..))?),
165 CT_FORWARD_TSN => {
166 Box::new(ChunkForwardTsn::unmarshal(&self.remaining.slice(offset..))?)
167 }
168 CT_I_FORWARD_TSN => Box::new(ChunkIForwardTsn::unmarshal(
169 &self.remaining.slice(offset..),
170 )?),
171 CT_ERROR => Box::new(ChunkError::unmarshal(&self.remaining.slice(offset..))?),
172 CT_SHUTDOWN => Box::new(ChunkShutdown::unmarshal(&self.remaining.slice(offset..))?),
173 CT_SHUTDOWN_ACK => Box::new(ChunkShutdownAck::unmarshal(
174 &self.remaining.slice(offset..),
175 )?),
176 CT_SHUTDOWN_COMPLETE => Box::new(ChunkShutdownComplete::unmarshal(
177 &self.remaining.slice(offset..),
178 )?),
179 _ => return Err(Error::ErrUnmarshalUnknownChunkType),
180 };
181 
182 let chunk_value_padding = get_padding_size(c.value_length());
183 offset += CHUNK_HEADER_SIZE + c.value_length() + chunk_value_padding;
184 chunks.push(c);
185 }
186 
187 Ok(Packet {
188 common_header: self.common_header,
189 chunks,
190 })
191 }
192}
193 
194#[derive(Default, Debug)]
195pub(crate) struct Packet {
196 pub(crate) common_header: CommonHeader,
197 pub(crate) chunks: Vec<Box<dyn Chunk + Send + Sync>>,
198}
199 
200/// makes packet printable
201impl fmt::Display for Packet {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 let mut res = format!(
204 "Packet:
205 source_port: {}
206 destination_port: {}
207 verification_tag: {}
208 ",
209 self.common_header.source_port,
210 self.common_header.destination_port,
211 self.common_header.verification_tag,
212 );
213 for chunk in &self.chunks {
214 res += format!("Chunk: {}", chunk).as_str();
215 }
216 write!(f, "{}", res)
217 }
218}
219 
220impl Packet {
221 pub(crate) fn unmarshal(raw: &Bytes) -> Result<Self> {
222 if raw.len() < PACKET_HEADER_SIZE {
223 return Err(Error::ErrPacketRawTooSmall);
224 }
225 
226 let reader = &mut raw.clone();
227 
228 let source_port = reader.get_u16();
229 let destination_port = reader.get_u16();
230 let verification_tag = reader.get_u32();
231 let their_checksum = reader.get_u32_le();
232 let our_checksum = generate_packet_checksum(raw);
233 
234 if their_checksum != our_checksum {
235 return Err(Error::ErrChecksumMismatch);
236 }
237 
238 let mut chunks = vec![];
239 let mut offset = PACKET_HEADER_SIZE;
240 loop {
241 // Exact match, no more chunks
242 if offset == raw.len() {
243 break;
244 } else if offset + CHUNK_HEADER_SIZE > raw.len() {
245 return Err(Error::ErrParseSctpChunkNotEnoughData);
246 }
247 
248 let ct = ChunkType(raw[offset]);
249 let c: Box<dyn Chunk + Send + Sync> = match ct {
250 CT_INIT => Box::new(ChunkInit::unmarshal(&raw.slice(offset..))?),
251 CT_INIT_ACK => Box::new(ChunkInit::unmarshal(&raw.slice(offset..))?),
252 CT_ABORT => Box::new(ChunkAbort::unmarshal(&raw.slice(offset..))?),
253 CT_COOKIE_ECHO => Box::new(ChunkCookieEcho::unmarshal(&raw.slice(offset..))?),
254 CT_COOKIE_ACK => Box::new(ChunkCookieAck::unmarshal(&raw.slice(offset..))?),
255 CT_HEARTBEAT => Box::new(ChunkHeartbeat::unmarshal(&raw.slice(offset..))?),
256 CT_PAYLOAD_DATA => Box::new(ChunkPayloadData::unmarshal(&raw.slice(offset..))?),
257 CT_SACK => Box::new(ChunkSelectiveAck::unmarshal(&raw.slice(offset..))?),
258 CT_RECONFIG => Box::new(ChunkReconfig::unmarshal(&raw.slice(offset..))?),
259 CT_FORWARD_TSN => Box::new(ChunkForwardTsn::unmarshal(&raw.slice(offset..))?),
260 CT_I_FORWARD_TSN => Box::new(ChunkIForwardTsn::unmarshal(&raw.slice(offset..))?),
261 CT_ERROR => Box::new(ChunkError::unmarshal(&raw.slice(offset..))?),
262 CT_SHUTDOWN => Box::new(ChunkShutdown::unmarshal(&raw.slice(offset..))?),
263 CT_SHUTDOWN_ACK => Box::new(ChunkShutdownAck::unmarshal(&raw.slice(offset..))?),
264 CT_SHUTDOWN_COMPLETE => {
265 Box::new(ChunkShutdownComplete::unmarshal(&raw.slice(offset..))?)
266 }
267 _ => return Err(Error::ErrUnmarshalUnknownChunkType),
268 };
269 
270 let chunk_value_padding = get_padding_size(c.value_length());
271 offset += CHUNK_HEADER_SIZE + c.value_length() + chunk_value_padding;
272 chunks.push(c);
273 }
274 
275 Ok(Packet {
276 common_header: CommonHeader {
277 source_port,
278 destination_port,
279 verification_tag,
280 },
281 chunks,
282 })
283 }
284 
285 pub(crate) fn marshal_to(&self, writer: &mut BytesMut) -> Result<usize> {
286 // Populate static headers
287 // 8-12 is Checksum which will be populated when packet is complete
288 writer.put_u16(self.common_header.source_port);
289 writer.put_u16(self.common_header.destination_port);
290 writer.put_u32(self.common_header.verification_tag);
291 
292 // This is where the checksum will be written
293 let checksum_pos = writer.len();
294 writer.extend_from_slice(&[0, 0, 0, 0]);
295 
296 // Populate chunks
297 for c in &self.chunks {
298 c.marshal_to(writer)?;
299 
300 let padding_needed = get_padding_size(writer.len());
301 if padding_needed != 0 {
302 // padding needed if < 4 because we pad to 4
303 writer.extend_from_slice(&[0u8; PADDING_MULTIPLE][..padding_needed]);
304 }
305 }
306 
307 let mut digest = ISCSI_CRC.digest();
308 digest.update(writer);
309 let checksum = digest.finalize();
310 
311 // Checksum is already in BigEndian
312 // Using LittleEndian stops it from being flipped
313 let checksum_place = &mut writer[checksum_pos..checksum_pos + 4];
314 checksum_place.copy_from_slice(&checksum.to_le_bytes());
315 
316 Ok(writer.len())
317 }
318 
319 pub(crate) fn marshal(&self) -> Result<Bytes> {
320 // Chunk lengths are known before writing. Reserve the complete padded
321 // packet once instead of growing from the common header for every send.
322 let capacity = self
323 .chunks
324 .iter()
325 .try_fold(PACKET_HEADER_SIZE, |total, chunk| {
326 let length = CHUNK_HEADER_SIZE
327 .checked_add(chunk.value_length())
328 .ok_or(Error::ErrOutboundPacketTooLarge)?;
329 total
330 .checked_add(length)
331 .and_then(|n| n.checked_add(get_padding_size(length)))
332 .ok_or(Error::ErrOutboundPacketTooLarge)
333 })?;
334 let mut buf = BytesMut::with_capacity(capacity);
335 self.marshal_to(&mut buf)?;
336 Ok(buf.freeze())
337 }
338}
339 
340impl Packet {
341 pub(crate) fn check_packet(&self) -> Result<()> {
342 // All packets must adhere to these rules
343 
344 // This is the SCTP sender's port number. It can be used by the
345 // receiver in combination with the source IP address, the SCTP
346 // destination port, and possibly the destination IP address to
347 // identify the association to which this packet belongs. The port
348 // number 0 MUST NOT be used.
349 if self.common_header.source_port == 0 {
350 return Err(Error::ErrSctpPacketSourcePortZero);
351 }
352 
353 // This is the SCTP port number to which this packet is destined.
354 // The receiving host will use this port number to de-multiplex the
355 // SCTP packet to the correct receiving endpoint/application. The
356 // port number 0 MUST NOT be used.
357 if self.common_header.destination_port == 0 {
358 return Err(Error::ErrSctpPacketDestinationPortZero);
359 }
360 
361 // Check values on the packet that are specific to a particular chunk type
362 for c in &self.chunks {
363 if let Some(ci) = c.as_any().downcast_ref::<ChunkInit>() {
364 if !ci.is_ack {
365 // An INIT or INIT ACK chunk MUST NOT be bundled with any other chunk.
366 // They MUST be the only chunks present in the SCTP packets that carry
367 // them.
368 if self.chunks.len() != 1 {
369 return Err(Error::ErrInitChunkBundled);
370 }
371 
372 // A packet containing an INIT chunk MUST have a zero Verification
373 // Tag.
374 if self.common_header.verification_tag != 0 {
375 return Err(Error::ErrInitChunkVerifyTagNotZero);
376 }
377 }
378 }
379 }
380 
381 Ok(())
382 }
383}
384 
385#[cfg(test)]
386mod test {
387 use super::*;
388 
389 #[test]
390 fn test_packet_unmarshal() -> Result<()> {
391 let result = Packet::unmarshal(&Bytes::new());
392 assert!(
393 result.is_err(),
394 "Unmarshal should fail when a packet is too small to be SCTP"
395 );
396 
397 let header_only = Bytes::from_static(&[
398 0x13, 0x88, 0x13, 0x88, 0x00, 0x00, 0x00, 0x00, 0x06, 0xa9, 0x00, 0xe1,
399 ]);
400 let pkt = Packet::unmarshal(&header_only)?;
401 //assert!(result.o(), "Unmarshal failed for SCTP packet with no chunks: {}", result);
402 assert_eq!(
403 pkt.common_header.source_port, 5000,
404 "Unmarshal passed for SCTP packet, but got incorrect source port exp: {} act: {}",
405 5000, pkt.common_header.source_port
406 );
407 assert_eq!(
408 pkt.common_header.destination_port, 5000,
409 "Unmarshal passed for SCTP packet, but got incorrect destination port exp: {} act: {}",
410 5000, pkt.common_header.destination_port
411 );
412 assert_eq!(
413 pkt.common_header.verification_tag, 0,
414 "Unmarshal passed for SCTP packet, but got incorrect verification tag exp: {} act: {}",
415 0, pkt.common_header.verification_tag
416 );
417 
418 let raw_chunk = Bytes::from_static(&[
419 0x13, 0x88, 0x13, 0x88, 0x00, 0x00, 0x00, 0x00, 0x81, 0x46, 0x9d, 0xfc, 0x01, 0x00,
420 0x00, 0x56, 0x55, 0xb9, 0x64, 0xa5, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00,
421 0xe8, 0x6d, 0x10, 0x30, 0xc0, 0x00, 0x00, 0x04, 0x80, 0x08, 0x00, 0x09, 0xc0, 0x0f,
422 0xc1, 0x80, 0x82, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x24, 0x9f, 0xeb, 0xbb, 0x5c,
423 0x50, 0xc9, 0xbf, 0x75, 0x9c, 0xb1, 0x2c, 0x57, 0x4f, 0xa4, 0x5a, 0x51, 0xba, 0x60,
424 0x17, 0x78, 0x27, 0x94, 0x5c, 0x31, 0xe6, 0x5d, 0x5b, 0x09, 0x47, 0xe2, 0x22, 0x06,
425 0x80, 0x04, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x06, 0x80, 0xc1,
426 0x00, 0x00,
427 ]);
428 
429 Packet::unmarshal(&raw_chunk)?;
430 
431 Ok(())
432 }
433 
434 #[test]
435 fn test_packet_marshal() -> Result<()> {
436 let header_only = Bytes::from_static(&[
437 0x13, 0x88, 0x13, 0x88, 0x00, 0x00, 0x00, 0x00, 0x06, 0xa9, 0x00, 0xe1,
438 ]);
439 let pkt = Packet::unmarshal(&header_only)?;
440 let header_only_marshaled = pkt.marshal()?;
441 assert_eq!(
442 header_only, header_only_marshaled,
443 "Unmarshal/Marshaled header only packet did not match \
444 \nheaderOnly: {:?} \nheader_only_marshaled {:?}",
445 header_only, header_only_marshaled
446 );
447 
448 Ok(())
449 }
450 
451 /*fn BenchmarkPacketGenerateChecksum(b *testing.B) {
452 var data [1024]byte
453
454 for i := 0; i < b.N; i++ {
455 _ = generatePacketChecksum(data[:])
456 }
457 }*/
458 
459 #[test]
460 fn test_partial_decode_init_chunk() -> Result<()> {
461 let raw_pkt = Bytes::from_static(&[
462 0x13, 0x88, 0x13, 0x88, 0x00, 0x00, 0x00, 0x00, 0x81, 0x46, 0x9d, 0xfc, 0x01, 0x00,
463 0x00, 0x56, 0x55, 0xb9, 0x64, 0xa5, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00,
464 0xe8, 0x6d, 0x10, 0x30, 0xc0, 0x00, 0x00, 0x04, 0x80, 0x08, 0x00, 0x09, 0xc0, 0x0f,
465 0xc1, 0x80, 0x82, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x24, 0x9f, 0xeb, 0xbb, 0x5c,
466 0x50, 0xc9, 0xbf, 0x75, 0x9c, 0xb1, 0x2c, 0x57, 0x4f, 0xa4, 0x5a, 0x51, 0xba, 0x60,
467 0x17, 0x78, 0x27, 0x94, 0x5c, 0x31, 0xe6, 0x5d, 0x5b, 0x09, 0x47, 0xe2, 0x22, 0x06,
468 0x80, 0x04, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x06, 0x80, 0xc1,
469 0x00, 0x00,
470 ]);
471 let pkt = PartialDecode::unmarshal(&raw_pkt)?;
472 
473 assert_eq!(pkt.first_chunk_type, CT_INIT);
474 if let Some(initiate_tag) = pkt.initiate_tag {
475 assert_eq!(
476 initiate_tag, 1438213285,
477 "Unmarshal passed for SCTP packet, but got incorrect initiate tag exp: {} act: {}",
478 1438213285, initiate_tag
479 );
480 }
481 
482 Ok(())
483 }
484 
485 #[test]
486 fn test_partial_decode_init_ack() -> Result<()> {
487 let raw_pkt = Bytes::from_static(&[
488 0x13, 0x88, 0x13, 0x88, 0xce, 0x15, 0x79, 0xa2, 0x96, 0x19, 0xe8, 0xb2, 0x02, 0x00,
489 0x00, 0x1c, 0xeb, 0x81, 0x4e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00,
490 0x50, 0xdf, 0x90, 0xd9, 0x00, 0x07, 0x00, 0x08, 0x94, 0x06, 0x2f, 0x93,
491 ]);
492 let pkt = PartialDecode::unmarshal(&raw_pkt)?;
493 
494 assert_eq!(pkt.first_chunk_type, CT_INIT_ACK);
495 if let Some(initiate_tag) = pkt.initiate_tag {
496 assert_eq!(
497 initiate_tag, 3951119873u32,
498 "Unmarshal passed for SCTP packet, but got incorrect initiate tag exp: {} act: {}",
499 3951119873u32, initiate_tag
500 );
501 }
502 
503 Ok(())
504 }
505}