use crate::ast::*;
use crate::builders::WireContext;
use crate::error::CompilerError;
use alloc::vec::Vec;
const CRC32C_TABLE: [u32; 256] = {
let mut table = [0u32; 256];
let mut i = 0;
while i < 256 {
let mut crc = i as u32;
let mut j = 0;
while j < 8 {
if crc & 1 != 0 {
crc = (crc >> 1) ^ 0x82F6_3B78;
} else {
crc >>= 1;
}
j += 1;
}
table[i] = crc;
i += 1;
}
table
};
pub fn crc32c(slices: &[&[u8]]) -> u32 {
let mut crc: u32 = 0xFFFF_FFFF;
for slice in slices {
for &byte in *slice {
let index = ((crc ^ (byte as u32)) & 0xFF) as usize;
crc = (crc >> 8) ^ CRC32C_TABLE[index];
}
}
crc ^ 0xFFFF_FFFF
}
pub fn build_sctp_payload(
ctx: &WireContext,
params: &SctpParams,
payload: &[u8],
) -> Result<(Vec<u8>, u8), CompilerError> {
let src_port = ctx.src_port;
let dst_port = ctx.dst_port;
let vtag = params.vtag.unwrap_or(0);
let mut sctp_packet = Vec::with_capacity(64 + payload.len());
sctp_packet.extend_from_slice(&src_port.to_be_bytes());
sctp_packet.extend_from_slice(&dst_port.to_be_bytes());
sctp_packet.extend_from_slice(&vtag.to_be_bytes());
sctp_packet.extend_from_slice(&0_u32.to_be_bytes());
let chunk_type = params.chunk.as_deref().unwrap_or("INIT");
match chunk_type.to_ascii_uppercase().as_str() {
"DATA" => {
let stream = params.stream.unwrap_or(0);
let ssn = params.ssn.unwrap_or(0);
let ppid = params.ppid.unwrap_or(0);
let chunk_len = (16 + payload.len()) as u16;
sctp_packet.push(0); sctp_packet.push(0x03); sctp_packet.extend_from_slice(&chunk_len.to_be_bytes());
sctp_packet.extend_from_slice(&1000_u32.to_be_bytes()); sctp_packet.extend_from_slice(&stream.to_be_bytes());
sctp_packet.extend_from_slice(&ssn.to_be_bytes());
sctp_packet.extend_from_slice(&ppid.to_be_bytes());
sctp_packet.extend_from_slice(payload);
while sctp_packet.len() % 4 != 0 {
sctp_packet.push(0);
}
}
"INIT_ACK" => {
sctp_packet.push(2); sctp_packet.push(0);
sctp_packet.extend_from_slice(&20_u16.to_be_bytes());
sctp_packet.extend_from_slice(&0x1234_5678_u32.to_be_bytes()); sctp_packet.extend_from_slice(&100_000_u32.to_be_bytes()); sctp_packet.extend_from_slice(&10_u16.to_be_bytes()); sctp_packet.extend_from_slice(&10_u16.to_be_bytes()); sctp_packet.extend_from_slice(&1000_u32.to_be_bytes()); }
"SACK" => {
sctp_packet.push(3); sctp_packet.push(0);
sctp_packet.extend_from_slice(&16_u16.to_be_bytes());
sctp_packet.extend_from_slice(&1000_u32.to_be_bytes()); sctp_packet.extend_from_slice(&100_000_u32.to_be_bytes()); sctp_packet.extend_from_slice(&0_u16.to_be_bytes()); sctp_packet.extend_from_slice(&0_u16.to_be_bytes()); }
"HEARTBEAT" => {
let chunk_len = (4 + payload.len()) as u16;
sctp_packet.push(4); sctp_packet.push(0);
sctp_packet.extend_from_slice(&chunk_len.to_be_bytes());
sctp_packet.extend_from_slice(payload);
while sctp_packet.len() % 4 != 0 {
sctp_packet.push(0);
}
}
"HEARTBEAT_ACK" => {
let chunk_len = (4 + payload.len()) as u16;
sctp_packet.push(5);
sctp_packet.push(0);
sctp_packet.extend_from_slice(&chunk_len.to_be_bytes());
sctp_packet.extend_from_slice(payload);
while sctp_packet.len() % 4 != 0 {
sctp_packet.push(0);
}
}
"ABORT" => {
sctp_packet.push(6);
sctp_packet.push(0);
sctp_packet.extend_from_slice(&4_u16.to_be_bytes());
}
"SHUTDOWN" => {
sctp_packet.push(7);
sctp_packet.push(0);
sctp_packet.extend_from_slice(&8_u16.to_be_bytes());
sctp_packet.extend_from_slice(&1000_u32.to_be_bytes()); }
"COOKIE_ECHO" => {
let chunk_len = (4 + payload.len()) as u16;
sctp_packet.push(10);
sctp_packet.push(0);
sctp_packet.extend_from_slice(&chunk_len.to_be_bytes());
sctp_packet.extend_from_slice(payload);
while sctp_packet.len() % 4 != 0 {
sctp_packet.push(0);
}
}
"COOKIE_ACK" => {
sctp_packet.push(11);
sctp_packet.push(0);
sctp_packet.extend_from_slice(&4_u16.to_be_bytes());
}
_ => {
sctp_packet.push(1); sctp_packet.push(0); sctp_packet.extend_from_slice(&20_u16.to_be_bytes()); sctp_packet.extend_from_slice(&0x1234_5678_u32.to_be_bytes()); sctp_packet.extend_from_slice(&100_000_u32.to_be_bytes()); sctp_packet.extend_from_slice(&10_u16.to_be_bytes()); sctp_packet.extend_from_slice(&10_u16.to_be_bytes()); sctp_packet.extend_from_slice(&1000_u32.to_be_bytes()); }
}
let cksum = crc32c(&[&sctp_packet]);
sctp_packet[8..12].copy_from_slice(&cksum.to_le_bytes());
Ok((sctp_packet, 132))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_crc32c_rfc_vectors() {
assert_eq!(crc32c(&[b"123456789"]), 0xe3069283);
assert_eq!(crc32c(&[&[0u8; 32]]), 0x8a9136aa);
}
#[test]
fn test_sctp_init_chunk_layout() {
let l2 = L2Modifiers::default();
let l3 = L3Modifiers::default();
let l4 = L4Modifiers::default();
let ctx = WireContext {
src_ip: "10.0.0.1".parse().unwrap(),
dst_ip: "10.0.0.2".parse().unwrap(),
src_mac: [0; 6],
dst_mac: [0; 6],
transport: None,
src_port: 2905,
dst_port: 2905,
seq: None,
ack: None,
l2: &l2,
l3: &l3,
l4: &l4,
};
let (pkt, proto) = build_sctp_payload(&ctx, &SctpParams::default(), &[]).unwrap();
assert_eq!(proto, 132);
assert_eq!(&pkt[0..2], &2905_u16.to_be_bytes());
assert_eq!(&pkt[2..4], &2905_u16.to_be_bytes());
assert_eq!(pkt[12], 1); }
}