use core::net::{Ipv4Addr, Ipv6Addr};
use crate::field::Field;
use crate::packet::{Layer, LayerContext};
use crate::protocols::ipsec::ikev2::payload::{
write_generic_payload_header, IkePayload, PayloadHeaderFields, PayloadType,
};
use crate::protocols::transport::common::{impl_layer_div, impl_layer_object};
use crate::CrafterError;
use crate::Result;
pub const IKE_IDI_PAYLOAD_NAME: &str = "IkeIdiPayload";
pub const IKE_IDR_PAYLOAD_NAME: &str = "IkeIdrPayload";
pub const ID_FIXED_LEN: usize = 4;
pub const ID_IPV4_ADDR: u8 = 1;
pub const ID_FQDN: u8 = 2;
pub const ID_RFC822_ADDR: u8 = 3;
pub const ID_IPV6_ADDR: u8 = 5;
pub const ID_KEY_ID: u8 = 11;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IdType {
Ipv4Addr,
Fqdn,
Rfc822Addr,
Ipv6Addr,
KeyId,
Unknown(u8),
}
impl IdType {
pub fn codepoint(self) -> u8 {
match self {
Self::Ipv4Addr => ID_IPV4_ADDR,
Self::Fqdn => ID_FQDN,
Self::Rfc822Addr => ID_RFC822_ADDR,
Self::Ipv6Addr => ID_IPV6_ADDR,
Self::KeyId => ID_KEY_ID,
Self::Unknown(value) => value,
}
}
}
impl From<u8> for IdType {
fn from(value: u8) -> Self {
match value {
ID_IPV4_ADDR => Self::Ipv4Addr,
ID_FQDN => Self::Fqdn,
ID_RFC822_ADDR => Self::Rfc822Addr,
ID_IPV6_ADDR => Self::Ipv6Addr,
ID_KEY_ID => Self::KeyId,
other => Self::Unknown(other),
}
}
}
impl From<IdType> for u8 {
fn from(id_type: IdType) -> Self {
id_type.codepoint()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IdRole {
Initiator,
Responder,
}
impl IdRole {
fn payload_type(self) -> PayloadType {
match self {
Self::Initiator => PayloadType::IdInitiator,
Self::Responder => PayloadType::IdResponder,
}
}
fn layer_name(self) -> &'static str {
match self {
Self::Initiator => IKE_IDI_PAYLOAD_NAME,
Self::Responder => IKE_IDR_PAYLOAD_NAME,
}
}
}
#[derive(Debug, Clone)]
pub struct IkeIdPayload {
role: IdRole,
id_type: Field<u8>,
id_data: Vec<u8>,
header: PayloadHeaderFields,
}
impl IkeIdPayload {
pub fn new(role: IdRole, id_type: impl Into<IdType>, id_data: impl Into<Vec<u8>>) -> Self {
Self {
role,
id_type: Field::user(id_type.into().codepoint()),
id_data: id_data.into(),
header: PayloadHeaderFields::new(),
}
}
pub fn initiator(id_type: impl Into<IdType>, id_data: impl Into<Vec<u8>>) -> Self {
Self::new(IdRole::Initiator, id_type, id_data)
}
pub fn responder(id_type: impl Into<IdType>, id_data: impl Into<Vec<u8>>) -> Self {
Self::new(IdRole::Responder, id_type, id_data)
}
pub fn initiator_ipv4(addr: Ipv4Addr) -> Self {
Self::initiator(IdType::Ipv4Addr, addr.octets().to_vec())
}
pub fn responder_ipv4(addr: Ipv4Addr) -> Self {
Self::responder(IdType::Ipv4Addr, addr.octets().to_vec())
}
pub fn initiator_ipv6(addr: Ipv6Addr) -> Self {
Self::initiator(IdType::Ipv6Addr, addr.octets().to_vec())
}
pub fn responder_ipv6(addr: Ipv6Addr) -> Self {
Self::responder(IdType::Ipv6Addr, addr.octets().to_vec())
}
pub fn initiator_fqdn(fqdn: impl AsRef<str>) -> Self {
Self::initiator(IdType::Fqdn, fqdn.as_ref().as_bytes().to_vec())
}
pub fn responder_fqdn(fqdn: impl AsRef<str>) -> Self {
Self::responder(IdType::Fqdn, fqdn.as_ref().as_bytes().to_vec())
}
pub fn id_type(mut self, id_type: impl Into<IdType>) -> Self {
self.id_type.set_user(id_type.into().codepoint());
self
}
pub fn id_data(mut self, id_data: impl Into<Vec<u8>>) -> Self {
self.id_data = id_data.into();
self
}
pub fn next_payload(mut self, next_payload: u8) -> Self {
self.header.set_next_payload(next_payload);
self
}
pub fn payload_length(mut self, length: u16) -> Self {
self.header.set_length(length);
self
}
pub fn critical(mut self, critical: bool) -> Self {
self.header.set_critical(critical);
self
}
pub fn role(&self) -> IdRole {
self.role
}
pub fn id_type_value(&self) -> u8 {
self.id_type.value().copied().unwrap_or(0)
}
pub fn id_type_kind(&self) -> IdType {
IdType::from(self.id_type_value())
}
pub fn id_data_bytes(&self) -> &[u8] {
&self.id_data
}
fn id_body(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(ID_FIXED_LEN + self.id_data.len());
out.push(self.id_type_value());
out.extend_from_slice(&[0u8, 0u8, 0u8]); out.extend_from_slice(&self.id_data);
out
}
}
impl IkePayload for IkeIdPayload {
fn payload_type(&self) -> PayloadType {
self.role.payload_type()
}
fn payload_body(&self, _ctx: &LayerContext<'_>) -> Result<Vec<u8>> {
Ok(self.id_body())
}
fn next_payload_override(&self) -> Option<u8> {
self.header.next_payload_override()
}
fn payload_length_override(&self) -> Option<u16> {
self.header.payload_length_override()
}
fn critical(&self) -> bool {
self.header.critical()
}
}
impl Layer for IkeIdPayload {
fn name(&self) -> &'static str {
self.role.layer_name()
}
fn summary(&self) -> String {
format!(
"{}(id_type={}, id_data_len={})",
self.role.layer_name(),
self.id_type_value(),
self.id_data.len()
)
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
vec![
("id_type", self.id_type_value().to_string()),
("id_data_len", self.id_data.len().to_string()),
]
}
fn encoded_len(&self) -> usize {
super::GENERIC_PAYLOAD_HEADER_LEN + ID_FIXED_LEN + self.id_data.len()
}
fn compile(&self, ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
let body = self.payload_body(ctx)?;
write_generic_payload_header(
out,
ctx,
self.next_payload_override(),
self.critical(),
self.payload_length_override(),
body.len(),
)?;
out.extend_from_slice(&body);
Ok(())
}
impl_layer_object!(IkeIdPayload);
}
impl_layer_div!(IkeIdPayload);
pub(crate) fn parse_id_payload_body(role: IdRole, bytes: &[u8]) -> Result<IkeIdPayload> {
if bytes.len() < ID_FIXED_LEN {
return Err(CrafterError::buffer_too_short(
"ikev2.id",
ID_FIXED_LEN,
bytes.len(),
));
}
let id_type = bytes[0];
let id_data = bytes[ID_FIXED_LEN..].to_vec();
Ok(IkeIdPayload::new(role, id_type, id_data))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::packet::{LayerContext, Packet, Raw};
use crate::protocols::ipsec::ikev2::payload::GENERIC_PAYLOAD_HEADER_LEN;
fn compile_payload(payload: IkeIdPayload) -> Vec<u8> {
let packet = Packet::from_layer(payload);
let ctx = LayerContext::new(&packet, 0);
let mut out = Vec::new();
packet.get(0).unwrap().compile(&ctx, &mut out).unwrap();
out
}
fn idi_ipv4_payload() -> IkeIdPayload {
IkeIdPayload::initiator_ipv4(Ipv4Addr::new(192, 0, 2, 1))
}
fn idr_fqdn_payload() -> IkeIdPayload {
IkeIdPayload::responder_fqdn("responder.example.com")
}
#[test]
fn id_constants_match_manifest() {
assert_eq!(ID_FIXED_LEN, 4);
assert_eq!(ID_IPV4_ADDR, 1);
assert_eq!(ID_FQDN, 2);
assert_eq!(ID_RFC822_ADDR, 3);
assert_eq!(ID_IPV6_ADDR, 5);
assert_eq!(ID_KEY_ID, 11);
assert_eq!(PayloadType::IdInitiator.codepoint(), 35);
assert_eq!(PayloadType::IdResponder.codepoint(), 36);
}
#[test]
fn id_type_round_trips_through_u8() {
for value in 0u8..=255 {
let id_type = IdType::from(value);
assert_eq!(id_type.codepoint(), value);
assert_eq!(u8::from(id_type), value);
}
}
#[test]
fn unknown_id_type_is_preserved() {
let unassigned = 200u8;
assert_eq!(IdType::from(unassigned), IdType::Unknown(unassigned));
assert_eq!(IdType::Unknown(unassigned).codepoint(), unassigned);
assert_eq!(IdType::from(4), IdType::Unknown(4));
}
#[test]
fn role_selects_payload_type_and_name() {
let idi = idi_ipv4_payload();
assert_eq!(idi.role(), IdRole::Initiator);
assert_eq!(idi.payload_type(), PayloadType::IdInitiator);
assert_eq!(idi.name(), IKE_IDI_PAYLOAD_NAME);
let idr = idr_fqdn_payload();
assert_eq!(idr.role(), IdRole::Responder);
assert_eq!(idr.payload_type(), PayloadType::IdResponder);
assert_eq!(idr.name(), IKE_IDR_PAYLOAD_NAME);
}
#[test]
fn idi_ipv4_body_lays_out_type_reserved_then_address() {
let payload = idi_ipv4_payload();
let body = payload.id_body();
assert_eq!(body[0], ID_IPV4_ADDR);
assert_eq!(&body[1..4], &[0, 0, 0]); assert_eq!(&body[ID_FIXED_LEN..], &[192, 0, 2, 1]);
assert_eq!(body.len(), ID_FIXED_LEN + 4);
assert_eq!(payload.id_type_kind(), IdType::Ipv4Addr);
}
#[test]
fn idr_fqdn_body_lays_out_type_reserved_then_name() {
let payload = idr_fqdn_payload();
let body = payload.id_body();
assert_eq!(body[0], ID_FQDN);
assert_eq!(&body[1..4], &[0, 0, 0]); assert_eq!(&body[ID_FIXED_LEN..], b"responder.example.com");
assert_eq!(payload.id_type_kind(), IdType::Fqdn);
}
#[test]
fn ipv6_constructor_uses_documentation_address() {
let addr: Ipv6Addr = "2001:db8::1".parse().unwrap();
let payload = IkeIdPayload::initiator_ipv6(addr);
assert_eq!(payload.id_type_value(), ID_IPV6_ADDR);
let body = payload.id_body();
assert_eq!(&body[ID_FIXED_LEN..], &addr.octets());
assert_eq!(body.len(), ID_FIXED_LEN + 16);
}
#[test]
fn payload_compiles_generic_header_then_body() {
let payload = idi_ipv4_payload();
let bytes = compile_payload(payload.clone());
assert_eq!(bytes[0], 0); assert_eq!(bytes[1], 0); let payload_len = u16::from_be_bytes([bytes[2], bytes[3]]) as usize;
assert_eq!(payload_len, bytes.len());
assert_eq!(payload_len, payload.encoded_len());
assert_eq!(&bytes[GENERIC_PAYLOAD_HEADER_LEN..], &payload.id_body()[..]);
}
#[test]
fn payload_honors_generic_header_overrides() {
let payload = idi_ipv4_payload()
.next_payload(39)
.critical(true)
.payload_length(0xBEEF);
let bytes = compile_payload(payload);
assert_eq!(bytes[0], 39);
assert_eq!(bytes[1], 0x80); assert_eq!(u16::from_be_bytes([bytes[2], bytes[3]]), 0xBEEF);
}
#[test]
fn payload_chain_next_payload_points_at_idi_and_idr() {
use crate::protocols::ipsec::ikev2::payload::{
following_next_payload, payload_type_for_layer_name, PAYLOAD_IDI, PAYLOAD_IDR,
};
assert_eq!(
payload_type_for_layer_name(IKE_IDI_PAYLOAD_NAME),
Some(PayloadType::IdInitiator)
);
assert_eq!(
payload_type_for_layer_name(IKE_IDR_PAYLOAD_NAME),
Some(PayloadType::IdResponder)
);
let idi_packet: Packet = Packet::from_layer(Raw::from_bytes([0u8; 0])) / idi_ipv4_payload();
let idi_ctx = LayerContext::new(&idi_packet, 0);
assert_eq!(following_next_payload(&idi_ctx), PAYLOAD_IDI);
let idr_packet: Packet = Packet::from_layer(Raw::from_bytes([0u8; 0])) / idr_fqdn_payload();
let idr_ctx = LayerContext::new(&idr_packet, 0);
assert_eq!(following_next_payload(&idr_ctx), PAYLOAD_IDR);
}
#[test]
fn round_trip_idi_ipv4_preserves_type_and_data() {
let payload = idi_ipv4_payload();
let bytes = compile_payload(payload.clone());
let parsed =
parse_id_payload_body(IdRole::Initiator, &bytes[GENERIC_PAYLOAD_HEADER_LEN..]).unwrap();
assert_eq!(parsed.role(), IdRole::Initiator);
assert_eq!(parsed.id_type_kind(), IdType::Ipv4Addr);
assert_eq!(parsed.id_data_bytes(), &[192, 0, 2, 1]);
let recompiled = compile_payload(parsed);
assert_eq!(recompiled, bytes);
}
#[test]
fn round_trip_idr_fqdn_preserves_type_and_data() {
let payload = idr_fqdn_payload();
let bytes = compile_payload(payload.clone());
let parsed =
parse_id_payload_body(IdRole::Responder, &bytes[GENERIC_PAYLOAD_HEADER_LEN..]).unwrap();
assert_eq!(parsed.role(), IdRole::Responder);
assert_eq!(parsed.id_type_kind(), IdType::Fqdn);
assert_eq!(parsed.id_data_bytes(), b"responder.example.com");
let recompiled = compile_payload(parsed);
assert_eq!(recompiled, bytes);
}
#[test]
fn parse_rejects_truncated_body() {
let err = parse_id_payload_body(IdRole::Initiator, &[1u8, 0, 0]).unwrap_err();
assert!(matches!(err, CrafterError::BufferTooShort { .. }));
}
}