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