use crate::field::Field;
use crate::packet::{Layer, LayerContext};
use crate::protocols::ipsec::ikev2::payload::following_next_payload;
use crate::protocols::transport::common::{impl_layer_div, impl_layer_object, payload_bytes_after};
use crate::Result;
pub const IKE_HEADER_LEN: usize = 28;
pub const IKE_VERSION_2: u8 = 0x20;
pub const NO_NEXT_PAYLOAD: u8 = 0;
pub const IKE_SA_INIT: u8 = 34;
pub const IKE_AUTH: u8 = 35;
pub const CREATE_CHILD_SA: u8 = 36;
pub const INFORMATIONAL: u8 = 37;
pub const IKE_FLAG_INITIATOR: u8 = 0x08;
pub const IKE_FLAG_VERSION: u8 = 0x10;
pub const IKE_FLAG_RESPONSE: u8 = 0x20;
#[derive(Debug, Clone)]
pub struct IkeHeader {
initiator_spi: Field<u64>,
responder_spi: Field<u64>,
next_payload: Field<u8>,
version: Field<u8>,
exchange_type: Field<u8>,
flags: Field<u8>,
message_id: Field<u32>,
length: Field<u32>,
}
const DEFAULT_RESPONDER_SPI: u64 = 0;
const DEFAULT_MESSAGE_ID: u32 = 0;
impl IkeHeader {
pub fn new() -> Self {
Self {
initiator_spi: Field::unset(),
responder_spi: Field::defaulted(DEFAULT_RESPONDER_SPI),
next_payload: Field::unset(),
version: Field::defaulted(IKE_VERSION_2),
exchange_type: Field::unset(),
flags: Field::unset(),
message_id: Field::defaulted(DEFAULT_MESSAGE_ID),
length: Field::unset(),
}
}
pub fn initiator_spi(mut self, initiator_spi: u64) -> Self {
self.initiator_spi.set_user(initiator_spi);
self
}
pub fn responder_spi(mut self, responder_spi: u64) -> Self {
self.responder_spi.set_user(responder_spi);
self
}
pub fn next_payload(mut self, next_payload: u8) -> Self {
self.next_payload.set_user(next_payload);
self
}
pub fn version(mut self, version: u8) -> Self {
self.version.set_user(version);
self
}
pub fn exchange(mut self, exchange_type: u8) -> Self {
self.exchange_type.set_user(exchange_type);
self
}
pub fn flags(mut self, flags: u8) -> Self {
self.flags.set_user(flags);
self
}
pub fn initiator(mut self) -> Self {
let flags = self.flags.value().copied().unwrap_or(0) | IKE_FLAG_INITIATOR;
self.flags.set_user(flags);
self
}
pub fn response(mut self) -> Self {
let flags = self.flags.value().copied().unwrap_or(0) | IKE_FLAG_RESPONSE;
self.flags.set_user(flags);
self
}
pub fn message_id(mut self, message_id: u32) -> Self {
self.message_id.set_user(message_id);
self
}
pub fn length(mut self, length: u32) -> Self {
self.length.set_user(length);
self
}
pub fn initiator_spi_value(&self) -> Option<u64> {
self.initiator_spi.value().copied()
}
pub fn responder_spi_value(&self) -> Option<u64> {
self.responder_spi.value().copied()
}
pub fn next_payload_value(&self) -> Option<u8> {
self.next_payload.value().copied()
}
pub fn version_value(&self) -> Option<u8> {
self.version.value().copied()
}
pub fn exchange_type_value(&self) -> Option<u8> {
self.exchange_type.value().copied()
}
pub fn flags_value(&self) -> Option<u8> {
self.flags.value().copied()
}
pub fn message_id_value(&self) -> Option<u32> {
self.message_id.value().copied()
}
pub fn length_value(&self) -> Option<u32> {
self.length.value().copied()
}
fn display_initiator_spi(&self) -> u64 {
self.initiator_spi.value().copied().unwrap_or(0)
}
fn display_responder_spi(&self) -> u64 {
self.responder_spi
.value()
.copied()
.unwrap_or(DEFAULT_RESPONDER_SPI)
}
fn display_version(&self) -> u8 {
self.version.value().copied().unwrap_or(IKE_VERSION_2)
}
fn display_exchange_type(&self) -> u8 {
self.exchange_type.value().copied().unwrap_or(0)
}
fn display_flags(&self) -> u8 {
self.flags.value().copied().unwrap_or(0)
}
fn display_message_id(&self) -> u32 {
self.message_id
.value()
.copied()
.unwrap_or(DEFAULT_MESSAGE_ID)
}
fn effective_next_payload(&self, ctx: &LayerContext<'_>) -> u8 {
if let Some(next_payload) = self.next_payload.value().copied() {
return next_payload;
}
following_next_payload(ctx)
}
fn effective_length(&self, ctx: &LayerContext<'_>) -> Result<u32> {
if let Some(length) = self.length.value().copied() {
return Ok(length);
}
let payload_len = payload_bytes_after(*ctx)?.len();
Ok((IKE_HEADER_LEN + payload_len) as u32)
}
}
impl Layer for IkeHeader {
fn name(&self) -> &'static str {
"IkeHeader"
}
fn summary(&self) -> String {
format!(
"IkeHeader(exchange={}, flags=0x{:02x}, msgid={}, ispi=0x{:016x}, rspi=0x{:016x})",
self.display_exchange_type(),
self.display_flags(),
self.display_message_id(),
self.display_initiator_spi(),
self.display_responder_spi(),
)
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
vec![
(
"initiator_spi",
format!("0x{:016x}", self.display_initiator_spi()),
),
(
"responder_spi",
format!("0x{:016x}", self.display_responder_spi()),
),
(
"next_payload",
self.next_payload
.value()
.map(|np| np.to_string())
.unwrap_or_else(|| "auto".to_string()),
),
("version", format!("0x{:02x}", self.display_version())),
("exchange_type", self.display_exchange_type().to_string()),
("flags", format!("0x{:02x}", self.display_flags())),
("message_id", self.display_message_id().to_string()),
(
"length",
self.length
.value()
.map(|len| len.to_string())
.unwrap_or_else(|| "auto".to_string()),
),
]
}
fn encoded_len(&self) -> usize {
IKE_HEADER_LEN
}
fn compile(&self, ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
let next_payload = self.effective_next_payload(ctx);
let length = self.effective_length(ctx)?;
out.reserve(IKE_HEADER_LEN);
out.extend_from_slice(&self.display_initiator_spi().to_be_bytes());
out.extend_from_slice(&self.display_responder_spi().to_be_bytes());
out.push(next_payload);
out.push(self.display_version());
out.push(self.display_exchange_type());
out.push(self.display_flags());
out.extend_from_slice(&self.display_message_id().to_be_bytes());
out.extend_from_slice(&length.to_be_bytes());
Ok(())
}
impl_layer_object!(IkeHeader);
}
impl_layer_div!(IkeHeader);
impl Default for IkeHeader {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn version_octet_matches_manifest() {
assert_eq!(IKE_VERSION_2, 0x20);
}
#[test]
fn exchange_types_match_iana_values() {
assert_eq!(IKE_SA_INIT, 34);
assert_eq!(IKE_AUTH, 35);
assert_eq!(CREATE_CHILD_SA, 36);
assert_eq!(INFORMATIONAL, 37);
}
#[test]
fn flag_bits_match_manifest() {
assert_eq!(IKE_FLAG_INITIATOR, 0x08);
assert_eq!(IKE_FLAG_VERSION, 0x10);
assert_eq!(IKE_FLAG_RESPONSE, 0x20);
}
#[test]
fn header_length_is_twenty_eight_octets() {
assert_eq!(IKE_HEADER_LEN, 28);
}
#[test]
fn new_applies_packet_builder_defaults() {
let header = IkeHeader::new();
assert_eq!(header.version_value(), Some(IKE_VERSION_2));
assert_eq!(header.responder_spi_value(), Some(0));
assert_eq!(header.message_id_value(), Some(0));
assert_eq!(header.initiator_spi_value(), None);
assert_eq!(header.next_payload_value(), None);
assert_eq!(header.exchange_type_value(), None);
assert_eq!(header.flags_value(), None);
assert_eq!(header.length_value(), None);
}
#[test]
fn setters_mark_fields_and_flag_helpers_or_in_bits() {
let header = IkeHeader::new()
.initiator_spi(0x0102_0304_0506_0708)
.responder_spi(0x1112_1314_1516_1718)
.exchange(IKE_SA_INIT)
.message_id(7)
.initiator()
.response();
assert_eq!(header.initiator_spi_value(), Some(0x0102_0304_0506_0708));
assert_eq!(header.responder_spi_value(), Some(0x1112_1314_1516_1718));
assert_eq!(header.exchange_type_value(), Some(IKE_SA_INIT));
assert_eq!(header.message_id_value(), Some(7));
assert_eq!(
header.flags_value(),
Some(IKE_FLAG_INITIATOR | IKE_FLAG_RESPONSE)
);
}
use crate::packet::{LayerContext, Packet, Raw};
fn compile_header(header: IkeHeader) -> Vec<u8> {
let packet = Packet::from_layer(header);
let ctx = LayerContext::new(&packet, 0);
let mut out = Vec::new();
packet.get(0).unwrap().compile(&ctx, &mut out).unwrap();
out
}
fn compile_header_with_body(header: IkeHeader, body: &[u8]) -> Vec<u8> {
let packet: Packet = Packet::from_layer(header) / Raw::from_bytes(body);
let ctx = LayerContext::new(&packet, 0);
let mut out = Vec::new();
packet.get(0).unwrap().compile(&ctx, &mut out).unwrap();
out
}
#[test]
fn compile_no_payloads_emits_length_twenty_eight() {
let header = IkeHeader::new().exchange(IKE_SA_INIT).initiator();
let bytes = compile_header(header);
assert_eq!(bytes.len(), IKE_HEADER_LEN);
assert_eq!(bytes[16], NO_NEXT_PAYLOAD);
assert_eq!(bytes[17], IKE_VERSION_2);
assert_eq!(bytes[18], IKE_SA_INIT);
assert_eq!(bytes[19], IKE_FLAG_INITIATOR);
assert_eq!(&bytes[24..28], &(IKE_HEADER_LEN as u32).to_be_bytes());
}
#[test]
fn compile_with_raw_payload_adds_payload_to_length() {
let body = [0xAAu8; 9];
let bytes = compile_header_with_body(IkeHeader::new().exchange(IKE_AUTH), &body);
assert_eq!(bytes.len(), IKE_HEADER_LEN);
let length = u32::from_be_bytes(bytes[24..28].try_into().unwrap());
assert_eq!(length as usize, IKE_HEADER_LEN + body.len());
}
#[test]
fn compile_keeps_caller_length_override() {
let body = [0xBBu8; 4];
let bytes =
compile_header_with_body(IkeHeader::new().exchange(IKE_SA_INIT).length(0x1234), &body);
let length = u32::from_be_bytes(bytes[24..28].try_into().unwrap());
assert_eq!(length, 0x1234);
}
#[test]
fn compile_keeps_caller_next_payload_override() {
let bytes = compile_header(IkeHeader::new().exchange(IKE_SA_INIT).next_payload(33));
assert_eq!(bytes[16], 33);
}
#[test]
fn compile_emits_spis_big_endian() {
let header = IkeHeader::new()
.initiator_spi(0x0102_0304_0506_0708)
.responder_spi(0x1112_1314_1516_1718)
.exchange(IKE_SA_INIT);
let bytes = compile_header(header);
assert_eq!(&bytes[0..8], &0x0102_0304_0506_0708u64.to_be_bytes());
assert_eq!(&bytes[8..16], &0x1112_1314_1516_1718u64.to_be_bytes());
}
}