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