use crate::field::Field;
use crate::packet::{Layer, LayerContext};
use crate::Result;
pub mod auth;
pub mod cert;
pub mod config;
pub mod delete;
pub mod eap;
pub mod encrypted;
pub mod id;
pub mod ke;
pub mod nonce;
pub mod notify;
pub mod sa;
pub mod ts;
pub mod vendor;
pub use auth::{AuthMethod, IkeAuthPayload};
pub use cert::{CertEncoding, IkeCertPayload, IkeCertReqPayload};
pub use config::{CfgType, ConfigAttribute, IkeConfigPayload};
pub use delete::IkeDeletePayload;
pub use eap::IkeEapPayload;
pub use encrypted::{DecodedSk, IkeEncryptedPayload};
pub use id::{IdRole, IdType, IkeIdPayload};
pub use ke::IkeKePayload;
pub use nonce::IkeNoncePayload;
pub use notify::{IkeNotifyPayload, NotifyType};
pub use sa::{IkeSaPayload, Proposal, Transform, TransformAttribute};
pub use ts::{IkeTsPayload, TrafficSelector, TsRole};
pub use vendor::IkeVendorIdPayload;
pub const GENERIC_PAYLOAD_HEADER_LEN: usize = 4;
pub const PAYLOAD_TYPE_NONE: u8 = 0;
pub const PAYLOAD_CRITICAL_BIT: u8 = 0x80;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PayloadType {
None,
SecurityAssociation,
KeyExchange,
IdInitiator,
IdResponder,
Certificate,
CertificateRequest,
Authentication,
Nonce,
Notify,
Delete,
VendorId,
TrafficSelectorInitiator,
TrafficSelectorResponder,
Encrypted,
Configuration,
ExtensibleAuthentication,
Unknown(u8),
}
pub const PAYLOAD_NONE: u8 = 0;
pub const PAYLOAD_SA: u8 = 33;
pub const PAYLOAD_KE: u8 = 34;
pub const PAYLOAD_IDI: u8 = 35;
pub const PAYLOAD_IDR: u8 = 36;
pub const PAYLOAD_CERT: u8 = 37;
pub const PAYLOAD_CERTREQ: u8 = 38;
pub const PAYLOAD_AUTH: u8 = 39;
pub const PAYLOAD_NONCE: u8 = 40;
pub const PAYLOAD_NOTIFY: u8 = 41;
pub const PAYLOAD_DELETE: u8 = 42;
pub const PAYLOAD_VENDOR_ID: u8 = 43;
pub const PAYLOAD_TSI: u8 = 44;
pub const PAYLOAD_TSR: u8 = 45;
pub const PAYLOAD_SK: u8 = 46;
pub const PAYLOAD_CP: u8 = 47;
pub const PAYLOAD_EAP: u8 = 48;
impl PayloadType {
pub fn codepoint(self) -> u8 {
match self {
Self::None => PAYLOAD_NONE,
Self::SecurityAssociation => PAYLOAD_SA,
Self::KeyExchange => PAYLOAD_KE,
Self::IdInitiator => PAYLOAD_IDI,
Self::IdResponder => PAYLOAD_IDR,
Self::Certificate => PAYLOAD_CERT,
Self::CertificateRequest => PAYLOAD_CERTREQ,
Self::Authentication => PAYLOAD_AUTH,
Self::Nonce => PAYLOAD_NONCE,
Self::Notify => PAYLOAD_NOTIFY,
Self::Delete => PAYLOAD_DELETE,
Self::VendorId => PAYLOAD_VENDOR_ID,
Self::TrafficSelectorInitiator => PAYLOAD_TSI,
Self::TrafficSelectorResponder => PAYLOAD_TSR,
Self::Encrypted => PAYLOAD_SK,
Self::Configuration => PAYLOAD_CP,
Self::ExtensibleAuthentication => PAYLOAD_EAP,
Self::Unknown(value) => value,
}
}
}
impl From<u8> for PayloadType {
fn from(value: u8) -> Self {
match value {
PAYLOAD_NONE => Self::None,
PAYLOAD_SA => Self::SecurityAssociation,
PAYLOAD_KE => Self::KeyExchange,
PAYLOAD_IDI => Self::IdInitiator,
PAYLOAD_IDR => Self::IdResponder,
PAYLOAD_CERT => Self::Certificate,
PAYLOAD_CERTREQ => Self::CertificateRequest,
PAYLOAD_AUTH => Self::Authentication,
PAYLOAD_NONCE => Self::Nonce,
PAYLOAD_NOTIFY => Self::Notify,
PAYLOAD_DELETE => Self::Delete,
PAYLOAD_VENDOR_ID => Self::VendorId,
PAYLOAD_TSI => Self::TrafficSelectorInitiator,
PAYLOAD_TSR => Self::TrafficSelectorResponder,
PAYLOAD_SK => Self::Encrypted,
PAYLOAD_CP => Self::Configuration,
PAYLOAD_EAP => Self::ExtensibleAuthentication,
other => Self::Unknown(other),
}
}
}
impl From<PayloadType> for u8 {
fn from(payload_type: PayloadType) -> Self {
payload_type.codepoint()
}
}
pub trait IkePayload: Layer {
fn payload_type(&self) -> PayloadType;
fn payload_body(&self, ctx: &LayerContext<'_>) -> Result<Vec<u8>>;
fn next_payload_override(&self) -> Option<u8> {
None
}
fn payload_length_override(&self) -> Option<u16> {
None
}
fn critical(&self) -> bool {
false
}
}
pub fn payload_type_for_layer_name(name: &str) -> Option<PayloadType> {
match name {
sa::IKE_SA_PAYLOAD_NAME => Some(PayloadType::SecurityAssociation),
ke::IKE_KE_PAYLOAD_NAME => Some(PayloadType::KeyExchange),
id::IKE_IDI_PAYLOAD_NAME => Some(PayloadType::IdInitiator),
id::IKE_IDR_PAYLOAD_NAME => Some(PayloadType::IdResponder),
cert::IKE_CERT_PAYLOAD_NAME => Some(PayloadType::Certificate),
cert::IKE_CERTREQ_PAYLOAD_NAME => Some(PayloadType::CertificateRequest),
auth::IKE_AUTH_PAYLOAD_NAME => Some(PayloadType::Authentication),
nonce::IKE_NONCE_PAYLOAD_NAME => Some(PayloadType::Nonce),
notify::IKE_NOTIFY_PAYLOAD_NAME => Some(PayloadType::Notify),
delete::IKE_DELETE_PAYLOAD_NAME => Some(PayloadType::Delete),
vendor::IKE_VENDOR_ID_PAYLOAD_NAME => Some(PayloadType::VendorId),
ts::IKE_TSI_PAYLOAD_NAME => Some(PayloadType::TrafficSelectorInitiator),
ts::IKE_TSR_PAYLOAD_NAME => Some(PayloadType::TrafficSelectorResponder),
config::IKE_CONFIG_PAYLOAD_NAME => Some(PayloadType::Configuration),
eap::IKE_EAP_PAYLOAD_NAME => Some(PayloadType::ExtensibleAuthentication),
encrypted::IKE_ENCRYPTED_PAYLOAD_NAME => Some(PayloadType::Encrypted),
_ => None,
}
}
pub fn following_ike_payload_type(ctx: &LayerContext<'_>) -> Option<PayloadType> {
let next = ctx.next()?;
payload_type_for_layer_name(next.name())
}
pub fn following_next_payload(ctx: &LayerContext<'_>) -> u8 {
following_ike_payload_type(ctx)
.map(PayloadType::codepoint)
.unwrap_or(PAYLOAD_TYPE_NONE)
}
pub fn write_generic_payload_header(
out: &mut Vec<u8>,
ctx: &LayerContext<'_>,
next_payload: Option<u8>,
critical: bool,
payload_length: Option<u16>,
body_len: usize,
) -> Result<()> {
let next_payload = next_payload.unwrap_or_else(|| following_next_payload(ctx));
let critical_octet = if critical { PAYLOAD_CRITICAL_BIT } else { 0 };
let length = payload_length.unwrap_or_else(|| (GENERIC_PAYLOAD_HEADER_LEN + body_len) as u16);
out.reserve(GENERIC_PAYLOAD_HEADER_LEN);
out.push(next_payload);
out.push(critical_octet);
out.extend_from_slice(&length.to_be_bytes());
Ok(())
}
#[derive(Debug, Clone)]
pub struct PayloadHeaderFields {
next_payload: Field<u8>,
length: Field<u16>,
critical: Field<bool>,
}
impl PayloadHeaderFields {
pub fn new() -> Self {
Self {
next_payload: Field::unset(),
length: Field::unset(),
critical: Field::defaulted(false),
}
}
pub fn set_next_payload(&mut self, next_payload: u8) {
self.next_payload.set_user(next_payload);
}
pub fn set_length(&mut self, length: u16) {
self.length.set_user(length);
}
pub fn set_critical(&mut self, critical: bool) {
self.critical.set_user(critical);
}
pub fn next_payload_override(&self) -> Option<u8> {
self.next_payload.value().copied()
}
pub fn payload_length_override(&self) -> Option<u16> {
self.length.value().copied()
}
pub fn critical(&self) -> bool {
self.critical.value().copied().unwrap_or(false)
}
}
impl Default for PayloadHeaderFields {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn payload_type_codepoints_match_manifest() {
assert_eq!(PayloadType::None.codepoint(), 0);
assert_eq!(PayloadType::SecurityAssociation.codepoint(), 33);
assert_eq!(PayloadType::KeyExchange.codepoint(), 34);
assert_eq!(PayloadType::IdInitiator.codepoint(), 35);
assert_eq!(PayloadType::IdResponder.codepoint(), 36);
assert_eq!(PayloadType::Certificate.codepoint(), 37);
assert_eq!(PayloadType::CertificateRequest.codepoint(), 38);
assert_eq!(PayloadType::Authentication.codepoint(), 39);
assert_eq!(PayloadType::Nonce.codepoint(), 40);
assert_eq!(PayloadType::Notify.codepoint(), 41);
assert_eq!(PayloadType::Delete.codepoint(), 42);
assert_eq!(PayloadType::VendorId.codepoint(), 43);
assert_eq!(PayloadType::TrafficSelectorInitiator.codepoint(), 44);
assert_eq!(PayloadType::TrafficSelectorResponder.codepoint(), 45);
assert_eq!(PayloadType::Encrypted.codepoint(), 46);
assert_eq!(PayloadType::Configuration.codepoint(), 47);
assert_eq!(PayloadType::ExtensibleAuthentication.codepoint(), 48);
}
#[test]
fn payload_type_constants_match_variants() {
assert_eq!(
PayloadType::from(PAYLOAD_SA),
PayloadType::SecurityAssociation
);
assert_eq!(PayloadType::from(PAYLOAD_SK), PayloadType::Encrypted);
assert_eq!(
PayloadType::from(PAYLOAD_EAP),
PayloadType::ExtensibleAuthentication
);
assert_eq!(PayloadType::None.codepoint(), PAYLOAD_TYPE_NONE);
}
#[test]
fn payload_type_round_trips_through_u8() {
for value in 0u8..=255 {
let payload_type = PayloadType::from(value);
assert_eq!(payload_type.codepoint(), value);
assert_eq!(u8::from(payload_type), value);
}
}
#[test]
fn unknown_payload_type_is_preserved() {
let unassigned = 200u8;
assert_eq!(
PayloadType::from(unassigned),
PayloadType::Unknown(unassigned)
);
assert_eq!(PayloadType::Unknown(unassigned).codepoint(), unassigned);
assert_eq!(PayloadType::from(0), PayloadType::None);
assert_eq!(PayloadType::from(49), PayloadType::Unknown(49));
}
#[allow(clippy::unnecessary_fallible_conversions)]
#[test]
fn try_from_u8_is_infallible_and_preserves_unknown() {
let known: PayloadType = u8::try_into(PAYLOAD_KE).unwrap();
assert_eq!(known, PayloadType::KeyExchange);
let unknown: PayloadType = u8::try_into(222u8).unwrap();
assert_eq!(unknown, PayloadType::Unknown(222));
}
#[test]
fn unknown_layer_name_has_no_payload_type() {
assert_eq!(payload_type_for_layer_name("Raw"), None);
assert_eq!(payload_type_for_layer_name("IkeHeader"), None);
}
#[test]
fn header_fields_defaults_and_overrides() {
let mut fields = PayloadHeaderFields::new();
assert_eq!(fields.next_payload_override(), None);
assert_eq!(fields.payload_length_override(), None);
assert!(!fields.critical());
fields.set_next_payload(33);
fields.set_length(0x1234);
fields.set_critical(true);
assert_eq!(fields.next_payload_override(), Some(33));
assert_eq!(fields.payload_length_override(), Some(0x1234));
assert!(fields.critical());
}
use crate::packet::{LayerContext, Packet, Raw};
#[test]
fn generic_header_auto_length_and_terminator_next_payload() {
let packet = Packet::from_layer(Raw::from_bytes([0u8; 0]));
let ctx = LayerContext::new(&packet, 0);
let mut out = Vec::new();
let body = [0xAAu8; 6];
write_generic_payload_header(&mut out, &ctx, None, false, None, body.len()).unwrap();
assert_eq!(out.len(), GENERIC_PAYLOAD_HEADER_LEN);
assert_eq!(out[0], PAYLOAD_TYPE_NONE); assert_eq!(out[1], 0); let length = u16::from_be_bytes([out[2], out[3]]);
assert_eq!(length as usize, GENERIC_PAYLOAD_HEADER_LEN + body.len());
}
#[test]
fn generic_header_honors_overrides() {
let packet = Packet::from_layer(Raw::from_bytes([0u8; 0]));
let ctx = LayerContext::new(&packet, 0);
let mut out = Vec::new();
write_generic_payload_header(&mut out, &ctx, Some(PAYLOAD_KE), true, Some(0x00FF), 6)
.unwrap();
assert_eq!(out[0], PAYLOAD_KE);
assert_eq!(out[1], PAYLOAD_CRITICAL_BIT);
assert_eq!(u16::from_be_bytes([out[2], out[3]]), 0x00FF);
}
#[test]
fn following_next_payload_is_terminator_for_non_payload_tail() {
let packet: Packet =
Packet::from_layer(Raw::from_bytes([1u8, 2, 3])) / Raw::from_bytes([4u8]);
let ctx = LayerContext::new(&packet, 0);
assert_eq!(following_ike_payload_type(&ctx), None);
assert_eq!(following_next_payload(&ctx), PAYLOAD_TYPE_NONE);
}
}