use crate::field::Field;
use crate::packet::{Layer, LayerContext};
use crate::protocols::transport::common::{hex_bytes, impl_layer_div, impl_layer_object};
use crate::Result;
pub const NON_ESP_MARKER_LEN: usize = 4;
pub const NON_ESP_MARKER: [u8; NON_ESP_MARKER_LEN] = [0, 0, 0, 0];
pub fn is_non_esp_marker(bytes: &[u8]) -> bool {
bytes.len() >= NON_ESP_MARKER_LEN && bytes[..NON_ESP_MARKER_LEN] == NON_ESP_MARKER
}
pub fn non_esp_marker() -> NatTraversal {
NatTraversal::marker()
}
#[derive(Debug, Clone)]
pub struct NatTraversal {
marker: Field<Vec<u8>>,
}
impl NatTraversal {
pub fn marker() -> Self {
Self {
marker: Field::defaulted(NON_ESP_MARKER.to_vec()),
}
}
pub fn bytes(mut self, marker: impl Into<Vec<u8>>) -> Self {
self.marker.set_user(marker.into());
self
}
pub fn marker_bytes(&self) -> &[u8] {
self.marker.value().map(Vec::as_slice).unwrap_or(&[])
}
}
impl Default for NatTraversal {
fn default() -> Self {
Self::marker()
}
}
impl Layer for NatTraversal {
fn name(&self) -> &'static str {
"NatTraversal"
}
fn summary(&self) -> String {
format!(
"NatTraversal(non-esp-marker={})",
hex_bytes(self.marker_bytes())
)
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
vec![("non_esp_marker", hex_bytes(self.marker_bytes()))]
}
fn encoded_len(&self) -> usize {
self.marker_bytes().len()
}
fn compile(&self, _ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
out.extend_from_slice(self.marker_bytes());
Ok(())
}
impl_layer_object!(NatTraversal);
}
impl_layer_div!(NatTraversal);
#[cfg(test)]
mod tests {
use super::*;
use crate::packet::{NetworkLayer, Packet, Raw};
use crate::protocols::ipsec::esp::Esp;
use crate::protocols::ipsec::ikev2::header::{IkeHeader, IKE_SA_INIT};
use crate::protocols::ipsec::ikev2::payload::ke::IkeKePayload;
use crate::protocols::ipsec::ikev2::payload::nonce::IkeNoncePayload;
use crate::protocols::ipsec::ikev2::payload::sa::{IkeSaPayload, Proposal, Transform};
use crate::protocols::ipv4::{Ipv4, IPPROTO_UDP};
use crate::protocols::transport::Udp;
const NATT_UDP_PORT: u16 = 4500;
#[test]
fn is_non_esp_marker_recognizes_four_zero_octets() {
assert!(is_non_esp_marker(&[0, 0, 0, 0]));
assert!(is_non_esp_marker(&[0, 0, 0, 0, 0x20, 0x22, 0x08, 0x00]));
assert!(!is_non_esp_marker(&[0x00, 0x00, 0x20, 0x00, 0xDE, 0xAD]));
assert!(!is_non_esp_marker(&[0x01, 0x00, 0x00, 0x00]));
assert!(!is_non_esp_marker(&[0, 0, 0]));
assert!(!is_non_esp_marker(&[]));
}
#[test]
fn marker_layer_compiles_to_four_zero_octets() {
let mut out = Vec::new();
let natt = NatTraversal::marker();
let packet = Packet::from_layer(natt);
let ctx = LayerContext::new(&packet, 0);
packet.get(0).unwrap().compile(&ctx, &mut out).unwrap();
assert_eq!(out, NON_ESP_MARKER.to_vec());
}
#[test]
fn marker_override_is_emitted_verbatim() {
let natt = NatTraversal::marker().bytes(vec![0x01, 0x02, 0x03, 0x04]);
assert_eq!(natt.marker_bytes(), &[0x01, 0x02, 0x03, 0x04]);
}
fn ike_sa_init_payloads() -> (IkeSaPayload, IkeKePayload, IkeNoncePayload) {
let proposal = Proposal::new(1, 3).with_transform(Transform::new(1, 20));
let sa = IkeSaPayload::new().with_proposal(proposal);
let ke = IkeKePayload::new(14, vec![0xAB; 32]);
let ni = IkeNoncePayload::new(vec![0x5A; 16]);
(sa, ke, ni)
}
#[test]
fn udp_4500_zero_marker_then_ike_decodes_to_ikev2_and_round_trips() {
let (sa, ke, ni) = ike_sa_init_payloads();
let ipv4 = Ipv4::new()
.protocol(IPPROTO_UDP)
.src("192.0.2.1".parse().unwrap())
.dst("192.0.2.2".parse().unwrap());
let udp = Udp::new().sport(NATT_UDP_PORT).dport(NATT_UDP_PORT);
let header = IkeHeader::new().exchange(IKE_SA_INIT).initiator();
let packet = Packet::from_layer(ipv4) / udp / non_esp_marker() / header / sa / ke / ni;
let wire = packet.compile().expect("compile NAT-T IKE").into_bytes();
let decoded =
Packet::decode_from_l3(NetworkLayer::Ipv4, &wire).expect("decode NAT-T IKE from L3");
assert!(
decoded.layer::<NatTraversal>().is_some(),
"non-ESP marker present"
);
assert_eq!(
decoded.layer::<NatTraversal>().unwrap().marker_bytes(),
&NON_ESP_MARKER
);
assert!(decoded.layer::<IkeHeader>().is_some(), "IkeHeader present");
assert!(decoded.layer::<IkeSaPayload>().is_some(), "SA present");
assert!(decoded.layer::<IkeKePayload>().is_some(), "KE present");
assert!(decoded.layer::<IkeNoncePayload>().is_some(), "Ni present");
assert!(
decoded.layer::<Esp>().is_none(),
"must not decode the marker+IKE as ESP"
);
let recompiled = decoded.compile().expect("recompile").into_bytes();
assert_eq!(recompiled, wire, "round-trip must be byte-exact");
}
#[test]
fn udp_4500_nonzero_leading_word_decodes_to_esp_and_round_trips() {
let ipv4 = Ipv4::new()
.protocol(IPPROTO_UDP)
.src("192.0.2.1".parse().unwrap())
.dst("192.0.2.2".parse().unwrap());
let udp = Udp::new().sport(NATT_UDP_PORT).dport(NATT_UDP_PORT);
let esp = Esp::new().spi(0x0000_2000).sequence(7);
let body = Raw::from_bytes(vec![0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04]);
let packet = Packet::from_layer(ipv4) / udp / esp / body;
let wire = packet.compile().expect("compile NAT-T ESP").into_bytes();
let decoded =
Packet::decode_from_l3(NetworkLayer::Ipv4, &wire).expect("decode NAT-T ESP from L3");
let esp = decoded
.layer::<Esp>()
.expect("UDP-encapsulated ESP decoded");
assert_eq!(esp.spi_value(), Some(0x0000_2000));
assert_eq!(esp.sequence_value(), Some(7));
assert!(esp.opaque_body().is_some());
assert!(
decoded.layer::<IkeHeader>().is_none(),
"must not decode an ESP SPI as an IKE header"
);
assert!(
decoded.layer::<NatTraversal>().is_none(),
"an ESP datagram carries no non-ESP marker"
);
let recompiled = decoded.compile().expect("recompile").into_bytes();
assert_eq!(recompiled, wire, "round-trip must be byte-exact");
}
}