crate::ix!();
#[inline(always)]
fn read_be32(b: &[u8]) -> u32 {
((b[0] as u32) << 24)
| ((b[1] as u32) << 16)
| ((b[2] as u32) << 8)
| (b[3] as u32)
}
impl NetAddr {
#[inline]
pub fn has_linked_ipv4(&self) -> bool {
self.is_routable()
&& (self.is_ipv4()
|| self.isrfc6145()
|| self.isrfc6052()
|| self.isrfc3964()
|| self.isrfc4380())
}
pub fn get_linked_ipv4(&self) -> u32 {
trace!(target: "netaddr", net = ?self.net(), "Deriving linked IPv4");
if self.is_ipv4() {
return read_be32(self.addr());
}
if self.isrfc6052() || self.isrfc6145() {
return read_be32(&self.addr()[12..]);
}
if self.isrfc3964() {
return read_be32(&self.addr()[2..6]);
}
if self.isrfc4380() {
return !read_be32(&self.addr()[12..]);
}
panic!("get_linked_ipv4() called on address without an embedded IPv4");
}
}
#[cfg(test)]
mod linked_ipv4_tests {
use super::*;
fn v4(o: [u8; 4]) -> NetAddr {
NetAddrBuilder::default()
.addr(PreVector::from(o.to_vec().as_slice()))
.net(Network::NET_IPV4)
.scope_id(0u32)
.build()
.unwrap()
}
fn v6(bytes: [u8; 16]) -> NetAddr {
NetAddrBuilder::default()
.addr(PreVector::from(bytes.to_vec().as_slice()))
.net(Network::NET_IPV6)
.scope_id(0u32)
.build()
.unwrap()
}
#[traced_test]
fn plain_ipv4_roundtrip() {
let ip = v4([8, 8, 4, 4]);
assert!(ip.has_linked_ipv4());
assert_eq!(ip.get_linked_ipv4(), 0x08080404);
}
#[traced_test]
fn rfc6052_mapping() {
let mut bytes = [0u8; 16];
bytes[..12].copy_from_slice(&[0x00, 0x64, 0xFF, 0x9B, 0, 0, 0, 0, 0, 0, 0, 0]);
bytes[12..].copy_from_slice(&[8, 8, 8, 8]);
let ip = v6(bytes);
assert!(ip.has_linked_ipv4());
assert_eq!(ip.get_linked_ipv4(), 0x08080808);
}
}
#[cfg(test)]
mod linked_ipv4_additional_spec {
use super::*;
fn v6(bytes: [u8; 16]) -> NetAddr {
NetAddrBuilder::default()
.addr(PreVector::from(&bytes[..]))
.net(Network::NET_IPV6)
.scope_id(0u32)
.build()
.unwrap()
}
#[traced_test]
fn rfc6145_translated_ipv4_is_last_four_bytes() {
let mut b = [0u8; 16];
b[..12].copy_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0x00, 0x00]);
b[12..].copy_from_slice(&[203, 0, 113, 6]);
let a = v6(b);
assert!(a.has_linked_ipv4());
let got = a.get_linked_ipv4();
info!(got_hex = format_args!("{:#010x}", got), "RFC6145 mapped IPv4");
assert_eq!(got, 0xCB007106);
}
#[traced_test]
fn rfc3964_6to4_extracts_middle_bytes() {
let mut b = [0u8; 16];
b[0] = 0x20;
b[1] = 0x02;
b[2..6].copy_from_slice(&[1, 2, 3, 4]);
let a = v6(b);
assert!(a.has_linked_ipv4());
let got = a.get_linked_ipv4();
info!(got_hex = format_args!("{:#010x}", got), "RFC3964 6to4 embedded IPv4");
assert_eq!(got, 0x01020304);
}
#[traced_test]
fn rfc4380_teredo_last_four_bytes_bitflipped() {
let mut b = [0u8; 16];
b[..4].copy_from_slice(&[0x20, 0x01, 0x00, 0x00]);
b[12..].copy_from_slice(&[!9u8, !8u8, !7u8, !6u8]);
let a = v6(b);
assert!(a.has_linked_ipv4());
let got = a.get_linked_ipv4();
info!(got_hex = format_args!("{:#010x}", got), "RFC4380 Teredo IPv4 (bit‑flipped)");
assert_eq!(got, 0x09080706);
}
#[traced_test]
fn rfc3964_6to4_extracts_embedded_ipv4() {
let mut b = [0u8; 16];
b[0..2].copy_from_slice(&[0x20, 0x02]);
b[2..6].copy_from_slice(&[1, 2, 3, 4]);
let ip = v6(b);
assert!(ip.has_linked_ipv4());
let linked = ip.get_linked_ipv4();
info!(linked_hex = format_args!("{:#x}", linked), "Linked IPv4 from 6to4");
assert_eq!(linked, 0x01020304);
}
#[traced_test]
fn teredo_extracts_inverted_tail_ipv4() {
let mut b = [0u8; 16];
b[0..4].copy_from_slice(&[0x20, 0x01, 0x00, 0x00]);
b[12..16].copy_from_slice(&[0xFE, 0xFD, 0xFC, 0xFB]);
let ip = v6(b);
assert!(ip.has_linked_ipv4());
let linked = ip.get_linked_ipv4();
debug!(linked_hex = format_args!("{:#x}", linked), "Linked IPv4 from Teredo");
assert_eq!(linked, 0x01020304);
}
}