Skip to main content

packet_strata/packet/tunnel/
teredo.rs

1//! Teredo tunneling protocol parser
2//!
3//! This module implements parsing for Teredo tunnels as defined in RFC 4380.
4//! Teredo allows IPv6 connectivity for nodes behind IPv4 NAT devices by
5//! encapsulating IPv6 packets in UDP datagrams.
6//!
7//! # Teredo Packet Format
8//!
9//! Teredo packets are carried over UDP (typically port 3544) and may contain
10//! optional indicators before the IPv6 payload:
11//!
12//! ```text
13//! +------------------+
14//! |   UDP Header     |
15//! +------------------+
16//! | Authentication   |  (optional, type 0x0001)
17//! +------------------+
18//! | Origin Indication|  (optional, type 0x0000)
19//! +------------------+
20//! |   IPv6 Packet    |
21//! +------------------+
22//! ```
23//!
24//! ## Origin Indication (8 bytes)
25//!
26//! ```text
27//!  0                   1                   2                   3
28//!  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
29//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30//! |         Type (0x0000)         |      Obfuscated Port          |
31//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
32//! |                   Obfuscated IPv4 Address                     |
33//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34//! ```
35//!
36//! ## Authentication (variable length)
37//!
38//! ```text
39//!  0                   1                   2                   3
40//!  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
41//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
42//! |         Type (0x0001)         |   ID-len      |  AU-len       |
43//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44//! |                     Client Identifier                         |
45//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46//! |                   Authentication Value                        |
47//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48//! |                         Nonce                                 |
49//! |                       (8 bytes)                               |
50//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
51//! |  Confirmation |
52//! +-+-+-+-+-+-+-+-+
53//! ```
54//!
55//! # Examples
56//!
57//! ## Basic Teredo parsing with Origin Indication
58//!
59//! ```
60//! use packet_strata::packet::tunnel::teredo::TeredoHeader;
61//! use packet_strata::packet::HeaderParser;
62//! use std::net::Ipv4Addr;
63//!
64//! // Teredo packet with origin indication
65//! let mut packet = Vec::new();
66//! // Type: Origin Indication (0x0000)
67//! packet.extend_from_slice(&0x0000u16.to_be_bytes());
68//! // Obfuscated port: 0x1234 XOR 0xFFFF
69//! packet.extend_from_slice(&(0xFFFFu16 ^ 0x1234u16).to_be_bytes());
70//! // Obfuscated IPv4: 192.168.1.100 XOR 0xFFFFFFFF
71//! packet.extend_from_slice(&(0xFFFFFFFFu32 ^ 0xC0A80164u32).to_be_bytes());
72//! // Add minimal IPv6 header
73//! packet.extend_from_slice(&[
74//!     0x60, 0x00, 0x00, 0x00,
75//!     0x00, 0x00, 0x3a, 0x40,
76//!     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
77//!     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
78//!     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79//!     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
80//! ]);
81//!
82//! let (header, ipv6_payload) = TeredoHeader::from_bytes(&packet).unwrap();
83//! assert_eq!(header.port(), 0x1234);
84//! assert_eq!(header.ipv4_addr(), Ipv4Addr::new(192, 168, 1, 100));
85//! ```
86//!
87//! ## Direct IPv6 parsing (no indicators)
88//!
89//! ```
90//! use packet_strata::packet::tunnel::teredo::TeredoPacket;
91//!
92//! // Direct IPv6 packet without Teredo indicators
93//! let packet = vec![
94//!     0x60, 0x00, 0x00, 0x00,  // IPv6: version=6, traffic class, flow label
95//!     0x00, 0x00, 0x3a, 0x40,  // payload length, next header (ICMPv6), hop limit
96//!     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97//!     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,  // src addr
98//!     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99//!     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,  // dst addr
100//! ];
101//!
102//! let teredo = TeredoPacket::parse(&packet).unwrap();
103//! assert!(teredo.authentication().is_none());
104//! assert!(teredo.origin_indication().is_none());
105//! ```
106
107use std::fmt::{self, Formatter};
108use std::net::Ipv4Addr;
109
110use zerocopy::byteorder::{BigEndian, U16, U32};
111use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
112
113use crate::packet::protocol::IpProto;
114use crate::packet::{HeaderParser, PacketHeader, PacketHeaderError};
115
116/// Teredo UDP port (server/relay)
117pub const TEREDO_PORT: u16 = 3544;
118
119/// Teredo indicator type: Origin Indication
120pub const TEREDO_TYPE_ORIGIN: u16 = 0x0000;
121
122/// Teredo indicator type: Authentication
123pub const TEREDO_TYPE_AUTH: u16 = 0x0001;
124
125/// IPv6 version nibble (used to detect start of IPv6 packet)
126const IPV6_VERSION_NIBBLE: u8 = 0x60;
127const IPV6_VERSION_MASK: u8 = 0xF0;
128
129/// Teredo Origin Indication header (8 bytes)
130///
131/// The origin indication contains the obfuscated (XOR'd) original
132/// source port and IPv4 address of the Teredo client.
133///
134/// This is the most common Teredo header format used in practice.
135///
136/// ```text
137///  0                   1                   2                   3
138///  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
139/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
140/// |         Type (0x0000)         |      Obfuscated Port          |
141/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
142/// |                   Obfuscated IPv4 Address                     |
143/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
144/// ```
145#[repr(C, packed)]
146#[derive(FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, KnownLayout, Immutable)]
147pub struct TeredoHeader {
148    indicator_type: U16<BigEndian>,
149    obfuscated_port: U16<BigEndian>,
150    obfuscated_ipv4: U32<BigEndian>,
151}
152
153impl TeredoHeader {
154    /// Returns the indicator type (should be 0x0000 for Origin Indication)
155    #[inline]
156    pub fn indicator_type(&self) -> u16 {
157        self.indicator_type.get()
158    }
159
160    /// Returns the obfuscated port value (XOR'd with 0xFFFF)
161    #[inline]
162    pub fn obfuscated_port(&self) -> u16 {
163        self.obfuscated_port.get()
164    }
165
166    /// Returns the original (de-obfuscated) port
167    ///
168    /// The port is obfuscated by XOR'ing with 0xFFFF
169    #[inline]
170    pub fn port(&self) -> u16 {
171        self.obfuscated_port.get() ^ 0xFFFF
172    }
173
174    /// Returns the obfuscated IPv4 address value (XOR'd with 0xFFFFFFFF)
175    #[inline]
176    pub fn obfuscated_ipv4(&self) -> u32 {
177        self.obfuscated_ipv4.get()
178    }
179
180    /// Returns the original (de-obfuscated) IPv4 address
181    ///
182    /// The address is obfuscated by XOR'ing with 0xFFFFFFFF
183    #[inline]
184    pub fn ipv4_addr(&self) -> Ipv4Addr {
185        Ipv4Addr::from(self.obfuscated_ipv4.get() ^ 0xFFFFFFFF)
186    }
187
188    /// Validates the origin indication header
189    #[inline]
190    fn is_valid(&self) -> bool {
191        self.indicator_type() == TEREDO_TYPE_ORIGIN
192    }
193}
194
195impl PacketHeader for TeredoHeader {
196    const NAME: &'static str = "TeredoHeader";
197    type InnerType = IpProto;
198
199    #[inline]
200    fn inner_type(&self) -> Self::InnerType {
201        // Teredo always encapsulates IPv6
202        IpProto::IPV6
203    }
204
205    #[inline]
206    fn total_len(&self, _buf: &[u8]) -> usize {
207        Self::FIXED_LEN
208    }
209
210    #[inline]
211    fn is_valid(&self) -> bool {
212        self.is_valid()
213    }
214}
215
216impl HeaderParser for TeredoHeader {
217    type Output<'a> = &'a TeredoHeader;
218
219    #[inline]
220    fn into_view<'a>(header: &'a Self, _raw_options: &'a [u8]) -> Self::Output<'a> {
221        header
222    }
223}
224
225impl fmt::Display for TeredoHeader {
226    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
227        write!(
228            f,
229            "Teredo origin port={} ipv4={}",
230            self.port(),
231            self.ipv4_addr()
232        )
233    }
234}
235
236/// Teredo Authentication header (fixed part, 4 bytes)
237///
238/// The authentication header has a fixed part followed by variable-length
239/// client identifier, authentication value, nonce, and confirmation byte.
240///
241/// ```text
242///  0                   1                   2                   3
243///  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
244/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
245/// |         Type (0x0001)         |   ID-len      |  AU-len       |
246/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
247/// ```
248#[repr(C, packed)]
249#[derive(FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, KnownLayout, Immutable)]
250pub struct TeredoAuthHeader {
251    indicator_type: U16<BigEndian>,
252    id_len: u8,
253    auth_len: u8,
254}
255
256impl TeredoAuthHeader {
257    /// Size of the fixed authentication header in bytes
258    pub const FIXED_SIZE: usize = 4;
259
260    /// Size of the nonce field in bytes
261    pub const NONCE_SIZE: usize = 8;
262
263    /// Size of the confirmation byte
264    pub const CONFIRMATION_SIZE: usize = 1;
265
266    /// Returns the indicator type (should be 0x0001)
267    #[inline]
268    pub fn indicator_type(&self) -> u16 {
269        self.indicator_type.get()
270    }
271
272    /// Returns the client identifier length
273    #[inline]
274    pub fn id_len(&self) -> u8 {
275        self.id_len
276    }
277
278    /// Returns the authentication value length
279    #[inline]
280    pub fn auth_len(&self) -> u8 {
281        self.auth_len
282    }
283
284    /// Returns the total length of the authentication header including variable parts
285    #[inline]
286    pub fn total_len(&self) -> usize {
287        Self::FIXED_SIZE
288            + self.id_len as usize
289            + self.auth_len as usize
290            + Self::NONCE_SIZE
291            + Self::CONFIRMATION_SIZE
292    }
293
294    /// Validates the authentication header
295    #[inline]
296    fn is_valid(&self) -> bool {
297        self.indicator_type() == TEREDO_TYPE_AUTH
298    }
299}
300
301impl PacketHeader for TeredoAuthHeader {
302    const NAME: &'static str = "TeredoAuthHeader";
303    type InnerType = IpProto;
304
305    #[inline]
306    fn inner_type(&self) -> Self::InnerType {
307        IpProto::IPV6
308    }
309
310    #[inline]
311    fn total_len(&self, _buf: &[u8]) -> usize {
312        self.total_len()
313    }
314
315    #[inline]
316    fn is_valid(&self) -> bool {
317        self.is_valid()
318    }
319}
320
321/// Teredo Authentication with parsed fields
322#[derive(Debug, Clone)]
323pub struct TeredoAuthHeaderFull<'a> {
324    /// Fixed header part
325    pub header: &'a TeredoAuthHeader,
326    /// Client identifier (variable length)
327    pub client_id: &'a [u8],
328    /// Authentication value (variable length)
329    pub auth_value: &'a [u8],
330    /// Nonce (8 bytes)
331    pub nonce: &'a [u8],
332    /// Confirmation byte
333    pub confirmation: u8,
334}
335
336impl<'a> TeredoAuthHeaderFull<'a> {
337    /// Returns the total length of this authentication header
338    #[inline]
339    pub fn total_len(&self) -> usize {
340        self.header.total_len()
341    }
342}
343
344impl std::ops::Deref for TeredoAuthHeaderFull<'_> {
345    type Target = TeredoAuthHeader;
346
347    #[inline]
348    fn deref(&self) -> &Self::Target {
349        self.header
350    }
351}
352
353impl HeaderParser for TeredoAuthHeader {
354    type Output<'a> = TeredoAuthHeaderFull<'a>;
355
356    fn into_view<'a>(header: &'a Self, options: &'a [u8]) -> Self::Output<'a> {
357        let id_len = header.id_len() as usize;
358        let auth_len = header.auth_len() as usize;
359
360        let client_id = &options[..id_len];
361        let auth_value = &options[id_len..id_len + auth_len];
362        let nonce_start = id_len + auth_len;
363        let nonce = &options[nonce_start..nonce_start + TeredoAuthHeader::NONCE_SIZE];
364        let confirmation = options[nonce_start + TeredoAuthHeader::NONCE_SIZE];
365
366        TeredoAuthHeaderFull {
367            header,
368            client_id,
369            auth_value,
370            nonce,
371            confirmation,
372        }
373    }
374}
375
376impl fmt::Display for TeredoAuthHeaderFull<'_> {
377    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
378        write!(
379            f,
380            "TeredoAuth id_len={} auth_len={} confirmation={}",
381            self.header.id_len(),
382            self.header.auth_len(),
383            self.confirmation
384        )
385    }
386}
387
388/// Teredo indicator types
389#[derive(Debug, Clone, Copy, PartialEq, Eq)]
390pub enum TeredoIndicator {
391    /// Origin Indication (type 0x0000)
392    Origin,
393    /// Authentication (type 0x0001)
394    Authentication,
395    /// Direct IPv6 packet (no indicator)
396    None,
397}
398
399impl TeredoIndicator {
400    /// Detect the indicator type from the first bytes
401    pub fn detect(buf: &[u8]) -> Option<Self> {
402        if buf.len() < 2 {
403            return None;
404        }
405
406        // Check if this is an IPv6 packet (version nibble = 6)
407        if (buf[0] & IPV6_VERSION_MASK) == IPV6_VERSION_NIBBLE {
408            return Some(TeredoIndicator::None);
409        }
410
411        let indicator_type = u16::from_be_bytes([buf[0], buf[1]]);
412
413        match indicator_type {
414            TEREDO_TYPE_ORIGIN => Some(TeredoIndicator::Origin),
415            TEREDO_TYPE_AUTH => Some(TeredoIndicator::Authentication),
416            _ => None, // Unknown indicator type
417        }
418    }
419}
420
421/// Parsed Teredo packet (for complex cases with optional Authentication)
422///
423/// Use this when you need to handle all Teredo variants including Authentication.
424/// For simple Origin Indication parsing, use `TeredoHeader::from_bytes()` directly.
425#[derive(Debug, Clone)]
426pub struct TeredoPacket<'a> {
427    /// Authentication header (if present)
428    authentication: Option<TeredoAuthHeaderFull<'a>>,
429    /// Origin indication header (if present)
430    origin_indication: Option<&'a TeredoHeader>,
431    /// IPv6 payload
432    ipv6_payload: &'a [u8],
433    /// Total header length (before IPv6)
434    header_len: usize,
435}
436
437impl<'a> TeredoPacket<'a> {
438    /// Parse a Teredo packet from the buffer (after UDP header)
439    ///
440    /// The buffer should contain the UDP payload of a Teredo packet.
441    /// This will parse any authentication and origin indication headers
442    /// and return the IPv6 payload.
443    pub fn parse(buf: &'a [u8]) -> Result<Self, PacketHeaderError> {
444        let mut offset = 0;
445        let mut authentication = None;
446        let mut origin_indication = None;
447
448        // First, check for Authentication header
449        if let Some(TeredoIndicator::Authentication) = TeredoIndicator::detect(&buf[offset..]) {
450            let (auth, rest) = TeredoAuthHeader::from_bytes(&buf[offset..])?;
451            offset = buf.len() - rest.len();
452            authentication = Some(auth);
453        }
454
455        // Then, check for Origin Indication
456        if let Some(TeredoIndicator::Origin) = TeredoIndicator::detect(&buf[offset..]) {
457            let (origin, _) = TeredoHeader::from_bytes(&buf[offset..])?;
458            origin_indication = Some(origin);
459            offset += TeredoHeader::FIXED_LEN;
460        }
461
462        // Verify we have an IPv6 packet
463        if buf.len() <= offset {
464            return Err(PacketHeaderError::TooShort("TeredoPacket"));
465        }
466
467        // Check for IPv6 version
468        if (buf[offset] & IPV6_VERSION_MASK) != IPV6_VERSION_NIBBLE {
469            return Err(PacketHeaderError::Invalid("TeredoPacket: not IPv6"));
470        }
471
472        let ipv6_payload = &buf[offset..];
473
474        Ok(TeredoPacket {
475            authentication,
476            origin_indication,
477            ipv6_payload,
478            header_len: offset,
479        })
480    }
481
482    /// Returns the authentication header if present
483    #[inline]
484    pub fn authentication(&self) -> Option<&TeredoAuthHeaderFull<'a>> {
485        self.authentication.as_ref()
486    }
487
488    /// Returns the origin indication header if present
489    #[inline]
490    pub fn origin_indication(&self) -> Option<&TeredoHeader> {
491        self.origin_indication
492    }
493
494    /// Returns the IPv6 payload
495    #[inline]
496    pub fn ipv6_payload(&self) -> &'a [u8] {
497        self.ipv6_payload
498    }
499
500    /// Returns the total Teredo header length (before IPv6)
501    #[inline]
502    pub fn header_len(&self) -> usize {
503        self.header_len
504    }
505
506    /// Returns true if any Teredo indicators are present
507    #[inline]
508    pub fn has_indicators(&self) -> bool {
509        self.authentication.is_some() || self.origin_indication.is_some()
510    }
511}
512
513impl fmt::Display for TeredoPacket<'_> {
514    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
515        write!(f, "Teredo")?;
516
517        if let Some(auth) = &self.authentication {
518            write!(f, " [{}]", auth)?;
519        }
520
521        if let Some(origin) = self.origin_indication {
522            write!(f, " [{}]", origin)?;
523        }
524
525        write!(f, " ipv6_len={}", self.ipv6_payload.len())
526    }
527}
528
529/// Check if a UDP packet might be Teredo based on ports
530#[inline]
531pub fn is_teredo_port(src_port: u16, dst_port: u16) -> bool {
532    src_port == TEREDO_PORT || dst_port == TEREDO_PORT
533}
534
535#[cfg(test)]
536mod tests {
537    use super::*;
538
539    fn create_minimal_ipv6() -> Vec<u8> {
540        vec![
541            0x60, 0x00, 0x00, 0x00, // version, traffic class, flow label
542            0x00, 0x00, 0x3a, 0x40, // payload length, next header (ICMPv6), hop limit
543            // Source address (::1)
544            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
545            0x00, 0x01, // Destination address (::1)
546            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
547            0x00, 0x01,
548        ]
549    }
550
551    #[test]
552    fn test_teredo_header_size() {
553        assert_eq!(std::mem::size_of::<TeredoHeader>(), 8);
554        assert_eq!(TeredoHeader::FIXED_LEN, 8);
555    }
556
557    #[test]
558    fn test_auth_header_size() {
559        assert_eq!(std::mem::size_of::<TeredoAuthHeader>(), 4);
560        assert_eq!(TeredoAuthHeader::FIXED_SIZE, 4);
561    }
562
563    #[test]
564    fn test_teredo_header_parsing() {
565        let header = TeredoHeader {
566            indicator_type: U16::new(TEREDO_TYPE_ORIGIN),
567            obfuscated_port: U16::new(0xFFFF ^ 0x1234), // Port 0x1234
568            obfuscated_ipv4: U32::new(0xFFFFFFFF ^ 0xC0A80164), // 192.168.1.100
569        };
570
571        assert_eq!(header.indicator_type(), TEREDO_TYPE_ORIGIN);
572        assert_eq!(header.port(), 0x1234);
573        assert_eq!(header.ipv4_addr(), Ipv4Addr::new(192, 168, 1, 100));
574        assert!(header.is_valid());
575    }
576
577    #[test]
578    fn test_teredo_header_obfuscation() {
579        // Port 80 obfuscated
580        let header = TeredoHeader {
581            indicator_type: U16::new(TEREDO_TYPE_ORIGIN),
582            obfuscated_port: U16::new(0xFFFF ^ 80),
583            obfuscated_ipv4: U32::new(0xFFFFFFFF ^ 0x0A000001), // 10.0.0.1
584        };
585
586        assert_eq!(header.port(), 80);
587        assert_eq!(header.ipv4_addr(), Ipv4Addr::new(10, 0, 0, 1));
588    }
589
590    #[test]
591    fn test_teredo_header_from_bytes() {
592        let mut packet = Vec::new();
593
594        // Origin Indication
595        packet.extend_from_slice(&0x0000u16.to_be_bytes()); // Type
596        packet.extend_from_slice(&(0xFFFFu16 ^ 0x1234).to_be_bytes()); // Obfuscated port
597        packet.extend_from_slice(&(0xFFFFFFFFu32 ^ 0xC0A80101).to_be_bytes()); // Obfuscated IPv4
598
599        // IPv6 header (payload)
600        packet.extend_from_slice(&create_minimal_ipv6());
601
602        let (header, ipv6_payload) = TeredoHeader::from_bytes(&packet).unwrap();
603        assert_eq!(header.port(), 0x1234);
604        assert_eq!(header.ipv4_addr(), Ipv4Addr::new(192, 168, 1, 1));
605        assert_eq!(ipv6_payload.len(), 40);
606        assert_eq!(header.inner_type(), IpProto::IPV6);
607    }
608
609    #[test]
610    fn test_teredo_header_invalid_type() {
611        let mut packet = Vec::new();
612
613        // Wrong type (Auth instead of Origin)
614        packet.extend_from_slice(&0x0001u16.to_be_bytes()); // Type = Auth
615        packet.extend_from_slice(&0xFFFFu16.to_be_bytes());
616        packet.extend_from_slice(&0xFFFFFFFFu32.to_be_bytes());
617
618        let result = TeredoHeader::from_bytes(&packet);
619        assert!(result.is_err());
620    }
621
622    #[test]
623    fn test_teredo_header_too_short() {
624        let packet = vec![0x00, 0x00, 0xFF, 0xFF]; // Only 4 bytes, need 8
625        let result = TeredoHeader::from_bytes(&packet);
626        assert!(result.is_err());
627    }
628
629    #[test]
630    fn test_indicator_detect_ipv6() {
631        let ipv6 = create_minimal_ipv6();
632        let indicator = TeredoIndicator::detect(&ipv6);
633        assert_eq!(indicator, Some(TeredoIndicator::None));
634    }
635
636    #[test]
637    fn test_indicator_detect_origin() {
638        let buf = vec![0x00, 0x00, 0xFF, 0xFF]; // Type 0x0000
639        let indicator = TeredoIndicator::detect(&buf);
640        assert_eq!(indicator, Some(TeredoIndicator::Origin));
641    }
642
643    #[test]
644    fn test_indicator_detect_auth() {
645        let buf = vec![0x00, 0x01, 0x00, 0x00]; // Type 0x0001
646        let indicator = TeredoIndicator::detect(&buf);
647        assert_eq!(indicator, Some(TeredoIndicator::Authentication));
648    }
649
650    #[test]
651    fn test_parse_direct_ipv6() {
652        let packet = create_minimal_ipv6();
653
654        let teredo = TeredoPacket::parse(&packet).unwrap();
655        assert!(teredo.authentication().is_none());
656        assert!(teredo.origin_indication().is_none());
657        assert!(!teredo.has_indicators());
658        assert_eq!(teredo.header_len(), 0);
659        assert_eq!(teredo.ipv6_payload().len(), 40);
660    }
661
662    #[test]
663    fn test_parse_with_origin_indication() {
664        let mut packet = Vec::new();
665
666        // Origin Indication
667        packet.extend_from_slice(&0x0000u16.to_be_bytes()); // Type
668        packet.extend_from_slice(&(0xFFFFu16 ^ 0x1234).to_be_bytes()); // Obfuscated port
669        packet.extend_from_slice(&(0xFFFFFFFFu32 ^ 0xC0A80101).to_be_bytes()); // Obfuscated IPv4
670
671        // IPv6 header
672        packet.extend_from_slice(&create_minimal_ipv6());
673
674        let teredo = TeredoPacket::parse(&packet).unwrap();
675        assert!(teredo.authentication().is_none());
676        assert!(teredo.origin_indication().is_some());
677        assert!(teredo.has_indicators());
678        assert_eq!(teredo.header_len(), 8);
679
680        let origin = teredo.origin_indication().unwrap();
681        assert_eq!(origin.port(), 0x1234);
682        assert_eq!(origin.ipv4_addr(), Ipv4Addr::new(192, 168, 1, 1));
683    }
684
685    #[test]
686    fn test_parse_with_authentication() {
687        let mut packet = Vec::new();
688
689        // Authentication header
690        packet.extend_from_slice(&0x0001u16.to_be_bytes()); // Type
691        packet.push(4); // ID-len
692        packet.push(8); // AU-len
693        packet.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]); // Client ID (4 bytes)
694        packet.extend_from_slice(&[0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8]); // Auth value (8 bytes)
695        packet.extend_from_slice(&[0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8]); // Nonce (8 bytes)
696        packet.push(0x00); // Confirmation
697
698        // IPv6 header
699        packet.extend_from_slice(&create_minimal_ipv6());
700
701        let teredo = TeredoPacket::parse(&packet).unwrap();
702        assert!(teredo.authentication().is_some());
703        assert!(teredo.origin_indication().is_none());
704        assert!(teredo.has_indicators());
705
706        let auth = teredo.authentication().unwrap();
707        assert_eq!(auth.header.id_len(), 4);
708        assert_eq!(auth.header.auth_len(), 8);
709        assert_eq!(auth.client_id, &[0x01, 0x02, 0x03, 0x04]);
710        assert_eq!(auth.nonce.len(), 8);
711        assert_eq!(auth.confirmation, 0x00);
712    }
713
714    #[test]
715    fn test_parse_with_auth_and_origin() {
716        let mut packet = Vec::new();
717
718        // Authentication header (minimal)
719        packet.extend_from_slice(&0x0001u16.to_be_bytes()); // Type
720        packet.push(0); // ID-len = 0
721        packet.push(0); // AU-len = 0
722        packet.extend_from_slice(&[0x00; 8]); // Nonce
723        packet.push(0x00); // Confirmation
724
725        // Origin Indication
726        packet.extend_from_slice(&0x0000u16.to_be_bytes()); // Type
727        packet.extend_from_slice(&(0xFFFFu16 ^ 3544).to_be_bytes()); // Teredo port
728        packet.extend_from_slice(&(0xFFFFFFFFu32 ^ 0x08080808).to_be_bytes()); // 8.8.8.8
729
730        // IPv6 header
731        packet.extend_from_slice(&create_minimal_ipv6());
732
733        let teredo = TeredoPacket::parse(&packet).unwrap();
734        assert!(teredo.authentication().is_some());
735        assert!(teredo.origin_indication().is_some());
736        assert!(teredo.has_indicators());
737
738        let origin = teredo.origin_indication().unwrap();
739        assert_eq!(origin.port(), 3544);
740        assert_eq!(origin.ipv4_addr(), Ipv4Addr::new(8, 8, 8, 8));
741    }
742
743    #[test]
744    fn test_parse_too_short() {
745        let packet = vec![0x00]; // Too short
746        assert!(TeredoPacket::parse(&packet).is_err());
747    }
748
749    #[test]
750    fn test_parse_no_ipv6() {
751        let packet = vec![
752            0x00, 0x00, // Origin indication type
753            0xFF, 0xFF, // Port
754            0xFF, 0xFF, 0xFF, 0xFF, // IPv4
755                  // Missing IPv6
756        ];
757        assert!(TeredoPacket::parse(&packet).is_err());
758    }
759
760    #[test]
761    fn test_parse_invalid_ipv6_version() {
762        let mut packet = vec![
763            0x00, 0x00, // Origin indication type
764            0xFF, 0xFF, // Port
765            0xFF, 0xFF, 0xFF, 0xFF, // IPv4
766        ];
767        // Invalid "IPv6" with version 4
768        packet.extend_from_slice(&[0x40, 0x00, 0x00, 0x00]);
769
770        assert!(TeredoPacket::parse(&packet).is_err());
771    }
772
773    #[test]
774    fn test_teredo_port_check() {
775        assert!(is_teredo_port(3544, 12345));
776        assert!(is_teredo_port(12345, 3544));
777        assert!(is_teredo_port(3544, 3544));
778        assert!(!is_teredo_port(80, 443));
779    }
780
781    #[test]
782    fn test_display_teredo_header() {
783        let header = TeredoHeader {
784            indicator_type: U16::new(TEREDO_TYPE_ORIGIN),
785            obfuscated_port: U16::new(0xFFFF ^ 1234),
786            obfuscated_ipv4: U32::new(0xFFFFFFFF ^ 0x0A000001),
787        };
788
789        let display = format!("{}", header);
790        assert!(display.contains("Teredo"));
791        assert!(display.contains("port=1234"));
792        assert!(display.contains("10.0.0.1"));
793    }
794
795    #[test]
796    fn test_display_packet() {
797        let packet = create_minimal_ipv6();
798        let teredo = TeredoPacket::parse(&packet).unwrap();
799
800        let display = format!("{}", teredo);
801        assert!(display.contains("Teredo"));
802        assert!(display.contains("ipv6_len=40"));
803    }
804
805    #[test]
806    fn test_auth_total_len() {
807        let fixed = TeredoAuthHeader {
808            indicator_type: U16::new(TEREDO_TYPE_AUTH),
809            id_len: 4,
810            auth_len: 8,
811        };
812
813        // 4 (fixed) + 4 (id) + 8 (auth) + 8 (nonce) + 1 (confirmation) = 25
814        assert_eq!(fixed.total_len(), 25);
815    }
816
817    #[test]
818    fn test_auth_minimal() {
819        let fixed = TeredoAuthHeader {
820            indicator_type: U16::new(TEREDO_TYPE_AUTH),
821            id_len: 0,
822            auth_len: 0,
823        };
824
825        // 4 (fixed) + 0 (id) + 0 (auth) + 8 (nonce) + 1 (confirmation) = 13
826        assert_eq!(fixed.total_len(), 13);
827        assert!(fixed.is_valid());
828    }
829
830    #[test]
831    fn test_auth_header_from_bytes() {
832        let mut packet = Vec::new();
833
834        // Authentication header
835        packet.extend_from_slice(&0x0001u16.to_be_bytes()); // Type
836        packet.push(2); // ID-len
837        packet.push(4); // AU-len
838        packet.extend_from_slice(&[0x01, 0x02]); // Client ID (2 bytes)
839        packet.extend_from_slice(&[0xA1, 0xA2, 0xA3, 0xA4]); // Auth value (4 bytes)
840        packet.extend_from_slice(&[0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8]); // Nonce
841        packet.push(0x42); // Confirmation
842
843        // Some payload
844        packet.extend_from_slice(&[0x60, 0x00]); // IPv6 start
845
846        let (auth, _payload) = TeredoAuthHeader::from_bytes(&packet).unwrap();
847        assert_eq!(auth.id_len(), 2);
848        assert_eq!(auth.auth_len(), 4);
849        assert_eq!(auth.client_id, &[0x01, 0x02]);
850        assert_eq!(auth.auth_value, &[0xA1, 0xA2, 0xA3, 0xA4]);
851        assert_eq!(auth.confirmation, 0x42);
852        assert_eq!(auth.inner_type(), IpProto::IPV6);
853    }
854
855    #[test]
856    fn test_real_world_teredo_address() {
857        // Teredo addresses encode the server, flags, NAT port, and client IPv4
858        // Example: server 65.54.227.120, port 40000, client 192.0.2.45
859
860        let header = TeredoHeader {
861            indicator_type: U16::new(TEREDO_TYPE_ORIGIN),
862            obfuscated_port: U16::new(0xFFFF ^ 40000),
863            obfuscated_ipv4: U32::new(0xFFFFFFFF ^ 0xC000022D), // 192.0.2.45
864        };
865
866        assert_eq!(header.port(), 40000);
867        assert_eq!(header.ipv4_addr(), Ipv4Addr::new(192, 0, 2, 45));
868    }
869}