sctp_async/
packet.rs

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