webrtc_sctp/
packet.rs

1use std::fmt;
2
3use bytes::{Buf, BufMut, Bytes, BytesMut};
4
5use crate::chunk::chunk_abort::ChunkAbort;
6use crate::chunk::chunk_cookie_ack::ChunkCookieAck;
7use crate::chunk::chunk_cookie_echo::ChunkCookieEcho;
8use crate::chunk::chunk_error::ChunkError;
9use crate::chunk::chunk_forward_tsn::ChunkForwardTsn;
10use crate::chunk::chunk_header::*;
11use crate::chunk::chunk_heartbeat::ChunkHeartbeat;
12use crate::chunk::chunk_init::ChunkInit;
13use crate::chunk::chunk_payload_data::ChunkPayloadData;
14use crate::chunk::chunk_reconfig::ChunkReconfig;
15use crate::chunk::chunk_selective_ack::ChunkSelectiveAck;
16use crate::chunk::chunk_shutdown::ChunkShutdown;
17use crate::chunk::chunk_shutdown_ack::ChunkShutdownAck;
18use crate::chunk::chunk_shutdown_complete::ChunkShutdownComplete;
19use crate::chunk::chunk_type::*;
20use crate::chunk::chunk_unknown::ChunkUnknown;
21use crate::chunk::Chunk;
22use crate::error::{Error, Result};
23use crate::util::*;
24
25/// Packet represents an SCTP packet
26///
27/// An SCTP packet is composed of a common header and chunks.  A chunk
28/// contains either control information or user data.
29///
30///
31///SCTP Packet Format
32/// 0                   1                   2                   3
33/// 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
34///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35///|                        Common Header                          |
36///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37///|                          Chunk #1                             |
38///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39///|                           ...                                 |
40///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41///|                          Chunk #n                             |
42///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43///
44///
45///SCTP Common Header Format
46///
47/// 0                   1                   2                   3
48/// 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
49///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
50///|     Source Value Number        |     Destination Value Number |
51///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52///|                      Verification Tag                         |
53///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
54///|                           Checksum                            |
55///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
56///
57/// ## Specifications
58///
59/// * [RFC 4960 §3]
60///
61/// [RFC 4960 §3]: https://tools.ietf.org/html/rfc4960#section-3
62#[derive(Default, Debug)]
63pub(crate) struct Packet {
64    pub(crate) source_port: u16,
65    pub(crate) destination_port: u16,
66    pub(crate) verification_tag: u32,
67    pub(crate) chunks: Vec<Box<dyn Chunk + Send + Sync>>,
68}
69
70/// makes packet printable
71impl fmt::Display for Packet {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        let mut res = format!(
74            "Packet:
75        source_port: {}
76        destination_port: {}
77        verification_tag: {}
78        ",
79            self.source_port, self.destination_port, self.verification_tag,
80        );
81        for chunk in &self.chunks {
82            res += format!("Chunk: {chunk}").as_str();
83        }
84        write!(f, "{res}")
85    }
86}
87
88pub(crate) const PACKET_HEADER_SIZE: usize = 12;
89
90impl Packet {
91    pub(crate) fn unmarshal(raw: &Bytes) -> Result<Self> {
92        if raw.len() < PACKET_HEADER_SIZE {
93            return Err(Error::ErrPacketRawTooSmall);
94        }
95
96        let reader = &mut raw.clone();
97
98        let source_port = reader.get_u16();
99        let destination_port = reader.get_u16();
100        let verification_tag = reader.get_u32();
101
102        #[cfg(not(fuzzing))]
103        // only check for checksums when we are not fuzzing. This lets the fuzzer test the code much easier without guessing correct checksums.
104        {
105            let their_checksum = reader.get_u32_le();
106            let our_checksum = generate_packet_checksum(raw);
107
108            if their_checksum != our_checksum {
109                return Err(Error::ErrChecksumMismatch);
110            }
111        }
112
113        let mut chunks = vec![];
114        let mut offset = PACKET_HEADER_SIZE;
115        loop {
116            // Exact match, no more chunks
117            if offset == raw.len() {
118                break;
119            } else if offset + CHUNK_HEADER_SIZE > raw.len() {
120                return Err(Error::ErrParseSctpChunkNotEnoughData);
121            }
122
123            let ct = ChunkType(raw[offset]);
124            let c: Box<dyn Chunk + Send + Sync> = match ct {
125                CT_INIT => Box::new(ChunkInit::unmarshal(&raw.slice(offset..))?),
126                CT_INIT_ACK => Box::new(ChunkInit::unmarshal(&raw.slice(offset..))?),
127                CT_ABORT => Box::new(ChunkAbort::unmarshal(&raw.slice(offset..))?),
128                CT_COOKIE_ECHO => Box::new(ChunkCookieEcho::unmarshal(&raw.slice(offset..))?),
129                CT_COOKIE_ACK => Box::new(ChunkCookieAck::unmarshal(&raw.slice(offset..))?),
130                CT_HEARTBEAT => Box::new(ChunkHeartbeat::unmarshal(&raw.slice(offset..))?),
131                CT_PAYLOAD_DATA => Box::new(ChunkPayloadData::unmarshal(&raw.slice(offset..))?),
132                CT_SACK => Box::new(ChunkSelectiveAck::unmarshal(&raw.slice(offset..))?),
133                CT_RECONFIG => Box::new(ChunkReconfig::unmarshal(&raw.slice(offset..))?),
134                CT_FORWARD_TSN => Box::new(ChunkForwardTsn::unmarshal(&raw.slice(offset..))?),
135                CT_ERROR => Box::new(ChunkError::unmarshal(&raw.slice(offset..))?),
136                CT_SHUTDOWN => Box::new(ChunkShutdown::unmarshal(&raw.slice(offset..))?),
137                CT_SHUTDOWN_ACK => Box::new(ChunkShutdownAck::unmarshal(&raw.slice(offset..))?),
138                CT_SHUTDOWN_COMPLETE => {
139                    Box::new(ChunkShutdownComplete::unmarshal(&raw.slice(offset..))?)
140                }
141                _ => Box::new(ChunkUnknown::unmarshal(&raw.slice(offset..))?),
142            };
143
144            let chunk_value_padding = get_padding_size(c.value_length());
145            offset += CHUNK_HEADER_SIZE + c.value_length() + chunk_value_padding;
146            chunks.push(c);
147        }
148
149        Ok(Packet {
150            source_port,
151            destination_port,
152            verification_tag,
153            chunks,
154        })
155    }
156
157    pub(crate) fn marshal_to(&self, writer: &mut BytesMut) -> Result<usize> {
158        // Populate static headers
159        // 8-12 is Checksum which will be populated when packet is complete
160        writer.put_u16(self.source_port);
161        writer.put_u16(self.destination_port);
162        writer.put_u32(self.verification_tag);
163
164        // This is where the checksum will be written
165        let checksum_pos = writer.len();
166        writer.extend_from_slice(&[0, 0, 0, 0]);
167
168        // Populate chunks
169        for c in &self.chunks {
170            c.marshal_to(writer)?;
171
172            let padding_needed = get_padding_size(writer.len());
173            if padding_needed != 0 {
174                // padding needed if < 4 because we pad to 4
175                writer.extend_from_slice(&[0u8; PADDING_MULTIPLE][..padding_needed]);
176            }
177        }
178
179        let mut digest = ISCSI_CRC.digest();
180        digest.update(writer);
181        let checksum = digest.finalize();
182
183        // Checksum is already in BigEndian
184        // Using LittleEndian stops it from being flipped
185        let checksum_place = &mut writer[checksum_pos..checksum_pos + 4];
186        checksum_place.copy_from_slice(&checksum.to_le_bytes());
187
188        Ok(writer.len())
189    }
190
191    pub(crate) fn marshal(&self) -> Result<Bytes> {
192        let mut buf = BytesMut::with_capacity(PACKET_HEADER_SIZE);
193        self.marshal_to(&mut buf)?;
194        Ok(buf.freeze())
195    }
196}
197
198impl Packet {
199    pub(crate) fn check_packet(&self) -> Result<()> {
200        // All packets must adhere to these rules
201
202        // This is the SCTP sender's port number.  It can be used by the
203        // receiver in combination with the source IP address, the SCTP
204        // destination port, and possibly the destination IP address to
205        // identify the association to which this packet belongs.  The port
206        // number 0 MUST NOT be used.
207        if self.source_port == 0 {
208            return Err(Error::ErrSctpPacketSourcePortZero);
209        }
210
211        // This is the SCTP port number to which this packet is destined.
212        // The receiving host will use this port number to de-multiplex the
213        // SCTP packet to the correct receiving endpoint/application.  The
214        // port number 0 MUST NOT be used.
215        if self.destination_port == 0 {
216            return Err(Error::ErrSctpPacketDestinationPortZero);
217        }
218
219        // Check values on the packet that are specific to a particular chunk type
220        for c in &self.chunks {
221            if let Some(ci) = c.as_any().downcast_ref::<ChunkInit>() {
222                if !ci.is_ack {
223                    // An INIT or INIT ACK chunk MUST NOT be bundled with any other chunk.
224                    // They MUST be the only chunks present in the SCTP packets that carry
225                    // them.
226                    if self.chunks.len() != 1 {
227                        return Err(Error::ErrInitChunkBundled);
228                    }
229
230                    // A packet containing an INIT chunk MUST have a zero Verification
231                    // Tag.
232                    if self.verification_tag != 0 {
233                        return Err(Error::ErrInitChunkVerifyTagNotZero);
234                    }
235                }
236            }
237        }
238
239        Ok(())
240    }
241}
242
243#[cfg(test)]
244mod test {
245    use super::*;
246
247    #[test]
248    fn test_packet_unmarshal() -> Result<()> {
249        let result = Packet::unmarshal(&Bytes::new());
250        assert!(
251            result.is_err(),
252            "Unmarshal should fail when a packet is too small to be SCTP"
253        );
254
255        let header_only = Bytes::from_static(&[
256            0x13, 0x88, 0x13, 0x88, 0x00, 0x00, 0x00, 0x00, 0x06, 0xa9, 0x00, 0xe1,
257        ]);
258        let pkt = Packet::unmarshal(&header_only)?;
259        //assert!(result.o(), "Unmarshal failed for SCTP packet with no chunks: {}", result);
260        assert_eq!(
261            pkt.source_port, 5000,
262            "Unmarshal passed for SCTP packet, but got incorrect source port exp: {} act: {}",
263            5000, pkt.source_port
264        );
265        assert_eq!(
266            pkt.destination_port, 5000,
267            "Unmarshal passed for SCTP packet, but got incorrect destination port exp: {} act: {}",
268            5000, pkt.destination_port
269        );
270        assert_eq!(
271            pkt.verification_tag, 0,
272            "Unmarshal passed for SCTP packet, but got incorrect verification tag exp: {} act: {}",
273            0, pkt.verification_tag
274        );
275
276        let raw_chunk = Bytes::from_static(&[
277            0x13, 0x88, 0x13, 0x88, 0x00, 0x00, 0x00, 0x00, 0x81, 0x46, 0x9d, 0xfc, 0x01, 0x00,
278            0x00, 0x56, 0x55, 0xb9, 0x64, 0xa5, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00,
279            0xe8, 0x6d, 0x10, 0x30, 0xc0, 0x00, 0x00, 0x04, 0x80, 0x08, 0x00, 0x09, 0xc0, 0x0f,
280            0xc1, 0x80, 0x82, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x24, 0x9f, 0xeb, 0xbb, 0x5c,
281            0x50, 0xc9, 0xbf, 0x75, 0x9c, 0xb1, 0x2c, 0x57, 0x4f, 0xa4, 0x5a, 0x51, 0xba, 0x60,
282            0x17, 0x78, 0x27, 0x94, 0x5c, 0x31, 0xe6, 0x5d, 0x5b, 0x09, 0x47, 0xe2, 0x22, 0x06,
283            0x80, 0x04, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x06, 0x80, 0xc1,
284            0x00, 0x00,
285        ]);
286
287        Packet::unmarshal(&raw_chunk)?;
288
289        Ok(())
290    }
291
292    #[test]
293    fn test_packet_marshal() -> Result<()> {
294        let header_only = Bytes::from_static(&[
295            0x13, 0x88, 0x13, 0x88, 0x00, 0x00, 0x00, 0x00, 0x06, 0xa9, 0x00, 0xe1,
296        ]);
297        let pkt = Packet::unmarshal(&header_only)?;
298        let header_only_marshaled = pkt.marshal()?;
299        assert_eq!(header_only, header_only_marshaled, "Unmarshal/Marshaled header only packet did not match \nheaderOnly: {header_only:?} \nheader_only_marshaled {header_only_marshaled:?}");
300
301        Ok(())
302    }
303
304    /*fn BenchmarkPacketGenerateChecksum(b *testing.B) {
305        var data [1024]byte
306
307        for i := 0; i < b.N; i++ {
308            _ = generatePacketChecksum(data[:])
309        }
310    }*/
311}