use std::fmt::{self, Formatter};
use std::net::Ipv4Addr;
use zerocopy::byteorder::{BigEndian, U16, U32};
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
use crate::packet::protocol::IpProto;
use crate::packet::{HeaderParser, PacketHeader, PacketHeaderError};
pub const TEREDO_PORT: u16 = 3544;
pub const TEREDO_TYPE_ORIGIN: u16 = 0x0000;
pub const TEREDO_TYPE_AUTH: u16 = 0x0001;
const IPV6_VERSION_NIBBLE: u8 = 0x60;
const IPV6_VERSION_MASK: u8 = 0xF0;
#[repr(C, packed)]
#[derive(FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, KnownLayout, Immutable)]
pub struct TeredoHeader {
indicator_type: U16<BigEndian>,
obfuscated_port: U16<BigEndian>,
obfuscated_ipv4: U32<BigEndian>,
}
impl TeredoHeader {
#[inline]
pub fn indicator_type(&self) -> u16 {
self.indicator_type.get()
}
#[inline]
pub fn obfuscated_port(&self) -> u16 {
self.obfuscated_port.get()
}
#[inline]
pub fn port(&self) -> u16 {
self.obfuscated_port.get() ^ 0xFFFF
}
#[inline]
pub fn obfuscated_ipv4(&self) -> u32 {
self.obfuscated_ipv4.get()
}
#[inline]
pub fn ipv4_addr(&self) -> Ipv4Addr {
Ipv4Addr::from(self.obfuscated_ipv4.get() ^ 0xFFFFFFFF)
}
#[inline]
fn is_valid(&self) -> bool {
self.indicator_type() == TEREDO_TYPE_ORIGIN
}
}
impl PacketHeader for TeredoHeader {
const NAME: &'static str = "TeredoHeader";
type InnerType = IpProto;
#[inline]
fn inner_type(&self) -> Self::InnerType {
IpProto::IPV6
}
#[inline]
fn total_len(&self, _buf: &[u8]) -> usize {
Self::FIXED_LEN
}
#[inline]
fn is_valid(&self) -> bool {
self.is_valid()
}
}
impl HeaderParser for TeredoHeader {
type Output<'a> = &'a TeredoHeader;
#[inline]
fn into_view<'a>(header: &'a Self, _raw_options: &'a [u8]) -> Self::Output<'a> {
header
}
}
impl fmt::Display for TeredoHeader {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"Teredo origin port={} ipv4={}",
self.port(),
self.ipv4_addr()
)
}
}
#[repr(C, packed)]
#[derive(FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, KnownLayout, Immutable)]
pub struct TeredoAuthHeader {
indicator_type: U16<BigEndian>,
id_len: u8,
auth_len: u8,
}
impl TeredoAuthHeader {
pub const FIXED_SIZE: usize = 4;
pub const NONCE_SIZE: usize = 8;
pub const CONFIRMATION_SIZE: usize = 1;
#[inline]
pub fn indicator_type(&self) -> u16 {
self.indicator_type.get()
}
#[inline]
pub fn id_len(&self) -> u8 {
self.id_len
}
#[inline]
pub fn auth_len(&self) -> u8 {
self.auth_len
}
#[inline]
pub fn total_len(&self) -> usize {
Self::FIXED_SIZE
+ self.id_len as usize
+ self.auth_len as usize
+ Self::NONCE_SIZE
+ Self::CONFIRMATION_SIZE
}
#[inline]
fn is_valid(&self) -> bool {
self.indicator_type() == TEREDO_TYPE_AUTH
}
}
impl PacketHeader for TeredoAuthHeader {
const NAME: &'static str = "TeredoAuthHeader";
type InnerType = IpProto;
#[inline]
fn inner_type(&self) -> Self::InnerType {
IpProto::IPV6
}
#[inline]
fn total_len(&self, _buf: &[u8]) -> usize {
self.total_len()
}
#[inline]
fn is_valid(&self) -> bool {
self.is_valid()
}
}
#[derive(Debug, Clone)]
pub struct TeredoAuthHeaderFull<'a> {
pub header: &'a TeredoAuthHeader,
pub client_id: &'a [u8],
pub auth_value: &'a [u8],
pub nonce: &'a [u8],
pub confirmation: u8,
}
impl<'a> TeredoAuthHeaderFull<'a> {
#[inline]
pub fn total_len(&self) -> usize {
self.header.total_len()
}
}
impl std::ops::Deref for TeredoAuthHeaderFull<'_> {
type Target = TeredoAuthHeader;
#[inline]
fn deref(&self) -> &Self::Target {
self.header
}
}
impl HeaderParser for TeredoAuthHeader {
type Output<'a> = TeredoAuthHeaderFull<'a>;
fn into_view<'a>(header: &'a Self, options: &'a [u8]) -> Self::Output<'a> {
let id_len = header.id_len() as usize;
let auth_len = header.auth_len() as usize;
let client_id = &options[..id_len];
let auth_value = &options[id_len..id_len + auth_len];
let nonce_start = id_len + auth_len;
let nonce = &options[nonce_start..nonce_start + TeredoAuthHeader::NONCE_SIZE];
let confirmation = options[nonce_start + TeredoAuthHeader::NONCE_SIZE];
TeredoAuthHeaderFull {
header,
client_id,
auth_value,
nonce,
confirmation,
}
}
}
impl fmt::Display for TeredoAuthHeaderFull<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"TeredoAuth id_len={} auth_len={} confirmation={}",
self.header.id_len(),
self.header.auth_len(),
self.confirmation
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TeredoIndicator {
Origin,
Authentication,
None,
}
impl TeredoIndicator {
pub fn detect(buf: &[u8]) -> Option<Self> {
if buf.len() < 2 {
return None;
}
if (buf[0] & IPV6_VERSION_MASK) == IPV6_VERSION_NIBBLE {
return Some(TeredoIndicator::None);
}
let indicator_type = u16::from_be_bytes([buf[0], buf[1]]);
match indicator_type {
TEREDO_TYPE_ORIGIN => Some(TeredoIndicator::Origin),
TEREDO_TYPE_AUTH => Some(TeredoIndicator::Authentication),
_ => None, }
}
}
#[derive(Debug, Clone)]
pub struct TeredoPacket<'a> {
authentication: Option<TeredoAuthHeaderFull<'a>>,
origin_indication: Option<&'a TeredoHeader>,
ipv6_payload: &'a [u8],
header_len: usize,
}
impl<'a> TeredoPacket<'a> {
pub fn parse(buf: &'a [u8]) -> Result<Self, PacketHeaderError> {
let mut offset = 0;
let mut authentication = None;
let mut origin_indication = None;
if let Some(TeredoIndicator::Authentication) = TeredoIndicator::detect(&buf[offset..]) {
let (auth, rest) = TeredoAuthHeader::from_bytes(&buf[offset..])?;
offset = buf.len() - rest.len();
authentication = Some(auth);
}
if let Some(TeredoIndicator::Origin) = TeredoIndicator::detect(&buf[offset..]) {
let (origin, _) = TeredoHeader::from_bytes(&buf[offset..])?;
origin_indication = Some(origin);
offset += TeredoHeader::FIXED_LEN;
}
if buf.len() <= offset {
return Err(PacketHeaderError::TooShort("TeredoPacket"));
}
if (buf[offset] & IPV6_VERSION_MASK) != IPV6_VERSION_NIBBLE {
return Err(PacketHeaderError::Invalid("TeredoPacket: not IPv6"));
}
let ipv6_payload = &buf[offset..];
Ok(TeredoPacket {
authentication,
origin_indication,
ipv6_payload,
header_len: offset,
})
}
#[inline]
pub fn authentication(&self) -> Option<&TeredoAuthHeaderFull<'a>> {
self.authentication.as_ref()
}
#[inline]
pub fn origin_indication(&self) -> Option<&TeredoHeader> {
self.origin_indication
}
#[inline]
pub fn ipv6_payload(&self) -> &'a [u8] {
self.ipv6_payload
}
#[inline]
pub fn header_len(&self) -> usize {
self.header_len
}
#[inline]
pub fn has_indicators(&self) -> bool {
self.authentication.is_some() || self.origin_indication.is_some()
}
}
impl fmt::Display for TeredoPacket<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Teredo")?;
if let Some(auth) = &self.authentication {
write!(f, " [{}]", auth)?;
}
if let Some(origin) = self.origin_indication {
write!(f, " [{}]", origin)?;
}
write!(f, " ipv6_len={}", self.ipv6_payload.len())
}
}
#[inline]
pub fn is_teredo_port(src_port: u16, dst_port: u16) -> bool {
src_port == TEREDO_PORT || dst_port == TEREDO_PORT
}
#[cfg(test)]
mod tests {
use super::*;
fn create_minimal_ipv6() -> Vec<u8> {
vec![
0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
]
}
#[test]
fn test_teredo_header_size() {
assert_eq!(std::mem::size_of::<TeredoHeader>(), 8);
assert_eq!(TeredoHeader::FIXED_LEN, 8);
}
#[test]
fn test_auth_header_size() {
assert_eq!(std::mem::size_of::<TeredoAuthHeader>(), 4);
assert_eq!(TeredoAuthHeader::FIXED_SIZE, 4);
}
#[test]
fn test_teredo_header_parsing() {
let header = TeredoHeader {
indicator_type: U16::new(TEREDO_TYPE_ORIGIN),
obfuscated_port: U16::new(0xFFFF ^ 0x1234), obfuscated_ipv4: U32::new(0xFFFFFFFF ^ 0xC0A80164), };
assert_eq!(header.indicator_type(), TEREDO_TYPE_ORIGIN);
assert_eq!(header.port(), 0x1234);
assert_eq!(header.ipv4_addr(), Ipv4Addr::new(192, 168, 1, 100));
assert!(header.is_valid());
}
#[test]
fn test_teredo_header_obfuscation() {
let header = TeredoHeader {
indicator_type: U16::new(TEREDO_TYPE_ORIGIN),
obfuscated_port: U16::new(0xFFFF ^ 80),
obfuscated_ipv4: U32::new(0xFFFFFFFF ^ 0x0A000001), };
assert_eq!(header.port(), 80);
assert_eq!(header.ipv4_addr(), Ipv4Addr::new(10, 0, 0, 1));
}
#[test]
fn test_teredo_header_from_bytes() {
let mut packet = Vec::new();
packet.extend_from_slice(&0x0000u16.to_be_bytes()); packet.extend_from_slice(&(0xFFFFu16 ^ 0x1234).to_be_bytes()); packet.extend_from_slice(&(0xFFFFFFFFu32 ^ 0xC0A80101).to_be_bytes());
packet.extend_from_slice(&create_minimal_ipv6());
let (header, ipv6_payload) = TeredoHeader::from_bytes(&packet).unwrap();
assert_eq!(header.port(), 0x1234);
assert_eq!(header.ipv4_addr(), Ipv4Addr::new(192, 168, 1, 1));
assert_eq!(ipv6_payload.len(), 40);
assert_eq!(header.inner_type(), IpProto::IPV6);
}
#[test]
fn test_teredo_header_invalid_type() {
let mut packet = Vec::new();
packet.extend_from_slice(&0x0001u16.to_be_bytes()); packet.extend_from_slice(&0xFFFFu16.to_be_bytes());
packet.extend_from_slice(&0xFFFFFFFFu32.to_be_bytes());
let result = TeredoHeader::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_teredo_header_too_short() {
let packet = vec![0x00, 0x00, 0xFF, 0xFF]; let result = TeredoHeader::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_indicator_detect_ipv6() {
let ipv6 = create_minimal_ipv6();
let indicator = TeredoIndicator::detect(&ipv6);
assert_eq!(indicator, Some(TeredoIndicator::None));
}
#[test]
fn test_indicator_detect_origin() {
let buf = vec![0x00, 0x00, 0xFF, 0xFF]; let indicator = TeredoIndicator::detect(&buf);
assert_eq!(indicator, Some(TeredoIndicator::Origin));
}
#[test]
fn test_indicator_detect_auth() {
let buf = vec![0x00, 0x01, 0x00, 0x00]; let indicator = TeredoIndicator::detect(&buf);
assert_eq!(indicator, Some(TeredoIndicator::Authentication));
}
#[test]
fn test_parse_direct_ipv6() {
let packet = create_minimal_ipv6();
let teredo = TeredoPacket::parse(&packet).unwrap();
assert!(teredo.authentication().is_none());
assert!(teredo.origin_indication().is_none());
assert!(!teredo.has_indicators());
assert_eq!(teredo.header_len(), 0);
assert_eq!(teredo.ipv6_payload().len(), 40);
}
#[test]
fn test_parse_with_origin_indication() {
let mut packet = Vec::new();
packet.extend_from_slice(&0x0000u16.to_be_bytes()); packet.extend_from_slice(&(0xFFFFu16 ^ 0x1234).to_be_bytes()); packet.extend_from_slice(&(0xFFFFFFFFu32 ^ 0xC0A80101).to_be_bytes());
packet.extend_from_slice(&create_minimal_ipv6());
let teredo = TeredoPacket::parse(&packet).unwrap();
assert!(teredo.authentication().is_none());
assert!(teredo.origin_indication().is_some());
assert!(teredo.has_indicators());
assert_eq!(teredo.header_len(), 8);
let origin = teredo.origin_indication().unwrap();
assert_eq!(origin.port(), 0x1234);
assert_eq!(origin.ipv4_addr(), Ipv4Addr::new(192, 168, 1, 1));
}
#[test]
fn test_parse_with_authentication() {
let mut packet = Vec::new();
packet.extend_from_slice(&0x0001u16.to_be_bytes()); packet.push(4); packet.push(8); packet.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]); packet.extend_from_slice(&[0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8]); packet.extend_from_slice(&[0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8]); packet.push(0x00);
packet.extend_from_slice(&create_minimal_ipv6());
let teredo = TeredoPacket::parse(&packet).unwrap();
assert!(teredo.authentication().is_some());
assert!(teredo.origin_indication().is_none());
assert!(teredo.has_indicators());
let auth = teredo.authentication().unwrap();
assert_eq!(auth.header.id_len(), 4);
assert_eq!(auth.header.auth_len(), 8);
assert_eq!(auth.client_id, &[0x01, 0x02, 0x03, 0x04]);
assert_eq!(auth.nonce.len(), 8);
assert_eq!(auth.confirmation, 0x00);
}
#[test]
fn test_parse_with_auth_and_origin() {
let mut packet = Vec::new();
packet.extend_from_slice(&0x0001u16.to_be_bytes()); packet.push(0); packet.push(0); packet.extend_from_slice(&[0x00; 8]); packet.push(0x00);
packet.extend_from_slice(&0x0000u16.to_be_bytes()); packet.extend_from_slice(&(0xFFFFu16 ^ 3544).to_be_bytes()); packet.extend_from_slice(&(0xFFFFFFFFu32 ^ 0x08080808).to_be_bytes());
packet.extend_from_slice(&create_minimal_ipv6());
let teredo = TeredoPacket::parse(&packet).unwrap();
assert!(teredo.authentication().is_some());
assert!(teredo.origin_indication().is_some());
assert!(teredo.has_indicators());
let origin = teredo.origin_indication().unwrap();
assert_eq!(origin.port(), 3544);
assert_eq!(origin.ipv4_addr(), Ipv4Addr::new(8, 8, 8, 8));
}
#[test]
fn test_parse_too_short() {
let packet = vec![0x00]; assert!(TeredoPacket::parse(&packet).is_err());
}
#[test]
fn test_parse_no_ipv6() {
let packet = vec![
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ];
assert!(TeredoPacket::parse(&packet).is_err());
}
#[test]
fn test_parse_invalid_ipv6_version() {
let mut packet = vec![
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ];
packet.extend_from_slice(&[0x40, 0x00, 0x00, 0x00]);
assert!(TeredoPacket::parse(&packet).is_err());
}
#[test]
fn test_teredo_port_check() {
assert!(is_teredo_port(3544, 12345));
assert!(is_teredo_port(12345, 3544));
assert!(is_teredo_port(3544, 3544));
assert!(!is_teredo_port(80, 443));
}
#[test]
fn test_display_teredo_header() {
let header = TeredoHeader {
indicator_type: U16::new(TEREDO_TYPE_ORIGIN),
obfuscated_port: U16::new(0xFFFF ^ 1234),
obfuscated_ipv4: U32::new(0xFFFFFFFF ^ 0x0A000001),
};
let display = format!("{}", header);
assert!(display.contains("Teredo"));
assert!(display.contains("port=1234"));
assert!(display.contains("10.0.0.1"));
}
#[test]
fn test_display_packet() {
let packet = create_minimal_ipv6();
let teredo = TeredoPacket::parse(&packet).unwrap();
let display = format!("{}", teredo);
assert!(display.contains("Teredo"));
assert!(display.contains("ipv6_len=40"));
}
#[test]
fn test_auth_total_len() {
let fixed = TeredoAuthHeader {
indicator_type: U16::new(TEREDO_TYPE_AUTH),
id_len: 4,
auth_len: 8,
};
assert_eq!(fixed.total_len(), 25);
}
#[test]
fn test_auth_minimal() {
let fixed = TeredoAuthHeader {
indicator_type: U16::new(TEREDO_TYPE_AUTH),
id_len: 0,
auth_len: 0,
};
assert_eq!(fixed.total_len(), 13);
assert!(fixed.is_valid());
}
#[test]
fn test_auth_header_from_bytes() {
let mut packet = Vec::new();
packet.extend_from_slice(&0x0001u16.to_be_bytes()); packet.push(2); packet.push(4); packet.extend_from_slice(&[0x01, 0x02]); packet.extend_from_slice(&[0xA1, 0xA2, 0xA3, 0xA4]); packet.extend_from_slice(&[0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8]); packet.push(0x42);
packet.extend_from_slice(&[0x60, 0x00]);
let (auth, _payload) = TeredoAuthHeader::from_bytes(&packet).unwrap();
assert_eq!(auth.id_len(), 2);
assert_eq!(auth.auth_len(), 4);
assert_eq!(auth.client_id, &[0x01, 0x02]);
assert_eq!(auth.auth_value, &[0xA1, 0xA2, 0xA3, 0xA4]);
assert_eq!(auth.confirmation, 0x42);
assert_eq!(auth.inner_type(), IpProto::IPV6);
}
#[test]
fn test_real_world_teredo_address() {
let header = TeredoHeader {
indicator_type: U16::new(TEREDO_TYPE_ORIGIN),
obfuscated_port: U16::new(0xFFFF ^ 40000),
obfuscated_ipv4: U32::new(0xFFFFFFFF ^ 0xC000022D), };
assert_eq!(header.port(), 40000);
assert_eq!(header.ipv4_addr(), Ipv4Addr::new(192, 0, 2, 45));
}
}