use p2p_foundation::tunneling::{
MapTunnel, MapProtocol, MapRule, PortParameters,
TunnelConfig, TunnelProtocol, TunnelState, Tunnel,
NetworkCapabilities, create_tunnel_config, create_tunnel
};
use p2p_foundation::Result;
use std::net::Ipv4Addr;
use std::time::Duration;
use tokio;
fn create_test_map_e_config() -> TunnelConfig {
TunnelConfig {
protocol: TunnelProtocol::MapE,
local_ipv4: Some("192.0.2.100".parse().unwrap()),
remote_ipv4: None,
ipv6_prefix: Some("2001:db8::".parse().unwrap()),
aftr_ipv6: None,
aftr_name: None,
mtu: 1460,
keepalive_interval: Duration::from_secs(30),
establishment_timeout: Duration::from_secs(10),
}
}
fn create_test_map_t_config() -> TunnelConfig {
TunnelConfig {
protocol: TunnelProtocol::MapT,
local_ipv4: Some("192.0.2.100".parse().unwrap()),
remote_ipv4: None,
ipv6_prefix: Some("2001:db8::".parse().unwrap()),
aftr_ipv6: None,
aftr_name: None,
mtu: 1500,
keepalive_interval: Duration::from_secs(30),
establishment_timeout: Duration::from_secs(10),
}
}
fn create_isp_capabilities() -> NetworkCapabilities {
NetworkCapabilities {
has_ipv4: true,
has_ipv6: true, behind_nat: false, public_ipv4: Some("192.0.2.100".parse().unwrap()),
ipv6_addresses: vec!["2001:db8::1".parse().unwrap()],
has_upnp: false,
interface_mtu: 1500,
}
}
fn create_customer_capabilities() -> NetworkCapabilities {
NetworkCapabilities {
has_ipv4: true,
has_ipv6: false, behind_nat: true, public_ipv4: Some("192.0.2.100".parse().unwrap()), ipv6_addresses: vec![],
has_upnp: false,
interface_mtu: 1500,
}
}
fn create_sample_map_rule() -> MapRule {
MapRule {
ipv6_prefix: "2001:db8::".parse().unwrap(),
ipv6_prefix_len: 32,
ipv4_prefix: "192.0.2.0".parse().unwrap(),
ipv4_prefix_len: 24,
port_params: PortParameters {
psid_offset: 4,
psid_length: 4,
excluded_ports: 1024,
},
border_relay: Some("2001:db8:ffff::1".parse().unwrap()),
is_fmr: true,
}
}
#[tokio::test]
async fn test_map_e_tunnel_creation() -> Result<()> {
let config = create_test_map_e_config();
let tunnel = MapTunnel::new(config, MapProtocol::MapE)?;
assert_eq!(tunnel.protocol(), TunnelProtocol::MapE);
assert_eq!(tunnel.state().await, TunnelState::Disconnected);
Ok(())
}
#[tokio::test]
async fn test_map_t_tunnel_creation() -> Result<()> {
let config = create_test_map_t_config();
let tunnel = MapTunnel::new(config, MapProtocol::MapT)?;
assert_eq!(tunnel.protocol(), TunnelProtocol::MapT);
assert_eq!(tunnel.state().await, TunnelState::Disconnected);
Ok(())
}
#[tokio::test]
async fn test_map_e_invalid_protocol() {
let mut config = create_test_map_e_config();
config.protocol = TunnelProtocol::MapT;
let result = MapTunnel::new(config, MapProtocol::MapE);
assert!(result.is_err());
}
#[tokio::test]
async fn test_map_t_invalid_protocol() {
let mut config = create_test_map_t_config();
config.protocol = TunnelProtocol::MapE;
let result = MapTunnel::new(config, MapProtocol::MapT);
assert!(result.is_err());
}
#[tokio::test]
async fn test_map_rule_configuration() -> Result<()> {
let config = create_test_map_e_config();
let mut tunnel = MapTunnel::new(config, MapProtocol::MapE)?;
let rule = create_sample_map_rule();
tunnel.add_map_rule(rule.clone());
Ok(())
}
#[tokio::test]
async fn test_ipv6_address_calculation() -> Result<()> {
let config = create_test_map_e_config();
let tunnel = MapTunnel::new(config, MapProtocol::MapE)?;
let rule = create_sample_map_rule();
let ipv4_addr: Ipv4Addr = "192.0.2.100".parse().unwrap();
let ipv6_addr = tunnel.calculate_ipv6_address(ipv4_addr, &rule)?;
let addr_str = ipv6_addr.to_string();
assert!(addr_str.starts_with("2001:db8"));
println!("Calculated IPv6 address: {}", ipv6_addr);
Ok(())
}
#[tokio::test]
async fn test_psid_extraction() -> Result<()> {
let config = create_test_map_e_config();
let tunnel = MapTunnel::new(config, MapProtocol::MapE)?;
let rule = create_sample_map_rule();
let ipv4_addr: Ipv4Addr = "192.0.2.100".parse().unwrap();
let psid = tunnel.extract_psid(ipv4_addr, &rule);
assert!(psid < (1 << rule.port_params.psid_length));
println!("Extracted PSID: {}", psid);
Ok(())
}
#[tokio::test]
async fn test_port_set_calculation() -> Result<()> {
let config = create_test_map_e_config();
let tunnel = MapTunnel::new(config, MapProtocol::MapE)?;
let rule = create_sample_map_rule();
let psid: u16 = 5;
let port_set = tunnel.calculate_port_set(psid, &rule);
assert_eq!(port_set.psid, psid);
assert!(port_set.start_port >= rule.port_params.excluded_ports);
assert!(port_set.port_count > 0);
assert!(!port_set.available_ports.is_empty());
println!("Port set for PSID {}: start={}, count={}, available={}",
psid, port_set.start_port, port_set.port_count, port_set.available_ports.len());
Ok(())
}
#[tokio::test]
async fn test_port_validation() -> Result<()> {
let config = create_test_map_e_config();
let mut tunnel = MapTunnel::new(config, MapProtocol::MapE)?;
let rule = create_sample_map_rule();
tunnel.add_map_rule(rule.clone());
let _ = tunnel.initialize_addresses().await;
let is_allowed = tunnel.is_port_allowed(8080);
println!("Port 8080 allowed: {}", is_allowed);
Ok(())
}
#[tokio::test]
async fn test_map_e_encapsulation() -> Result<()> {
let config = create_test_map_e_config();
let mut tunnel = MapTunnel::new(config, MapProtocol::MapE)?;
let rule = create_sample_map_rule();
tunnel.add_map_rule(rule);
let _ = tunnel.initialize_addresses().await;
let ipv4_packet = vec![
0x45, 0x00, 0x00, 0x3c, 0x12, 0x34, 0x40, 0x00, 0x40, 0x06, 0x00, 0x00, 192, 0, 2, 100, 192, 0, 2, 1, 0x1f, 0x90, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, ];
match tunnel.encapsulate_ipv4_in_ipv6(&ipv4_packet) {
Ok(ipv6_packet) => {
assert!(ipv6_packet.len() > 40); assert_eq!(ipv6_packet[0] & 0xF0, 0x60); assert_eq!(ipv6_packet[6], 4);
println!("MAP-E encapsulation successful: {} bytes", ipv6_packet.len());
}
Err(e) => {
println!("MAP-E encapsulation failed as expected: {}", e);
}
}
Ok(())
}
#[tokio::test]
async fn test_map_t_translation() -> Result<()> {
let config = create_test_map_t_config();
let mut tunnel = MapTunnel::new(config, MapProtocol::MapT)?;
let rule = create_sample_map_rule();
tunnel.add_map_rule(rule);
let _ = tunnel.initialize_addresses().await;
let ipv4_packet = vec![
0x45, 0x00, 0x00, 0x28, 0x12, 0x34, 0x40, 0x00, 0x40, 0x11, 0x00, 0x00, 192, 0, 2, 100, 192, 0, 2, 1, 0x1f, 0x90, 0x00, 0x35, 0x00, 0x08, 0x00, 0x00, ];
match tunnel.translate_ipv4_to_ipv6(&ipv4_packet) {
Ok(ipv6_packet) => {
assert!(ipv6_packet.len() >= 40); assert_eq!(ipv6_packet[0] & 0xF0, 0x60); assert_eq!(ipv6_packet[6], 17);
println!("MAP-T translation successful: {} bytes", ipv6_packet.len());
}
Err(e) => {
println!("MAP-T translation failed as expected: {}", e);
}
}
Ok(())
}
#[tokio::test]
async fn test_map_tunnel_factory() -> Result<()> {
let map_e_config = create_test_map_e_config();
let map_e_tunnel = create_tunnel(map_e_config)?;
assert_eq!(map_e_tunnel.protocol(), TunnelProtocol::MapE);
let map_t_config = create_test_map_t_config();
let map_t_tunnel = create_tunnel(map_t_config)?;
assert_eq!(map_t_tunnel.protocol(), TunnelProtocol::MapT);
Ok(())
}
#[tokio::test]
async fn test_map_config_generation() -> Result<()> {
let isp_caps = create_isp_capabilities();
let map_e_config = create_tunnel_config(TunnelProtocol::MapE, &isp_caps);
assert_eq!(map_e_config.protocol, TunnelProtocol::MapE);
assert_eq!(map_e_config.mtu, 1460); assert_eq!(map_e_config.local_ipv4, Some("192.0.2.100".parse().unwrap()));
let map_t_config = create_tunnel_config(TunnelProtocol::MapT, &isp_caps);
assert_eq!(map_t_config.protocol, TunnelProtocol::MapT);
assert_eq!(map_t_config.mtu, 1500); assert_eq!(map_t_config.local_ipv4, Some("192.0.2.100".parse().unwrap()));
Ok(())
}
#[tokio::test]
async fn test_map_tunnel_lifecycle() -> Result<()> {
let config = create_test_map_e_config();
let mut tunnel = MapTunnel::new(config, MapProtocol::MapE)?;
let rule = create_sample_map_rule();
tunnel.add_map_rule(rule);
assert_eq!(tunnel.state().await, TunnelState::Disconnected);
assert!(!tunnel.is_active().await);
match tunnel.connect().await {
Ok(_) => {
assert_eq!(tunnel.state().await, TunnelState::Connected);
assert!(tunnel.is_active().await);
let ipv6_addr = tunnel.local_ipv6_addr().await?;
let ipv4_addr = tunnel.local_ipv4_addr().await?;
println!("MAP tunnel connected: IPv6={}, IPv4={}", ipv6_addr, ipv4_addr);
tunnel.disconnect().await?;
assert_eq!(tunnel.state().await, TunnelState::Disconnected);
assert!(!tunnel.is_active().await);
}
Err(e) => {
println!("MAP connection failed as expected: {}", e);
let state = tunnel.state().await;
if let TunnelState::Failed(reason) = state {
assert!(
reason.contains("address") ||
reason.contains("MAP") ||
reason.contains("Socket") ||
reason.contains("initialization")
);
}
}
}
Ok(())
}
#[tokio::test]
async fn test_map_tunnel_metrics() -> Result<()> {
let config = create_test_map_e_config();
let tunnel = MapTunnel::new(config, MapProtocol::MapE)?;
let metrics = tunnel.metrics().await;
assert_eq!(metrics.bytes_sent, 0);
assert_eq!(metrics.bytes_received, 0);
assert_eq!(metrics.packets_sent, 0);
assert_eq!(metrics.packets_received, 0);
Ok(())
}
#[tokio::test]
async fn test_map_tunnel_maintenance() -> Result<()> {
let config = create_test_map_t_config();
let mut tunnel = MapTunnel::new(config, MapProtocol::MapT)?;
tunnel.maintain().await?;
assert_eq!(tunnel.state().await, TunnelState::Disconnected);
Ok(())
}
#[tokio::test]
async fn test_map_error_handling() -> Result<()> {
let config = create_test_map_e_config();
let mut tunnel = MapTunnel::new(config, MapProtocol::MapE)?;
let send_result = tunnel.send(&[1, 2, 3, 4]).await;
assert!(send_result.is_err());
assert!(send_result.unwrap_err().to_string().contains("socket not available"));
let receive_result = tunnel.receive().await;
assert!(receive_result.is_err());
assert!(receive_result.unwrap_err().to_string().contains("socket not available"));
let ping_result = tunnel.ping(Duration::from_secs(1)).await;
match ping_result {
Ok(rtt) => println!("MAP ping succeeded: {:?}", rtt),
Err(e) => println!("MAP ping failed as expected: {}", e),
}
Ok(())
}
#[tokio::test]
async fn test_map_rule_validation() -> Result<()> {
let config = create_test_map_e_config();
let tunnel = MapTunnel::new(config, MapProtocol::MapE)?;
let invalid_rule = MapRule {
ipv6_prefix: "2001:db8::".parse().unwrap(),
ipv6_prefix_len: 32,
ipv4_prefix: "192.0.2.0".parse().unwrap(),
ipv4_prefix_len: 30, port_params: PortParameters {
psid_offset: 4,
psid_length: 4, excluded_ports: 1024,
},
border_relay: Some("2001:db8:ffff::1".parse().unwrap()),
is_fmr: true,
};
let ipv4_addr: Ipv4Addr = "192.0.2.1".parse().unwrap();
let result = tunnel.calculate_ipv6_address(ipv4_addr, &invalid_rule);
assert!(result.is_err());
Ok(())
}
#[tokio::test]
async fn test_map_customer_integration() -> Result<()> {
let customer_caps = create_customer_capabilities();
let map_e_config = create_tunnel_config(TunnelProtocol::MapE, &customer_caps);
assert_eq!(map_e_config.protocol, TunnelProtocol::MapE);
assert_eq!(map_e_config.local_ipv4, Some("192.0.2.100".parse().unwrap()));
let map_t_config = create_tunnel_config(TunnelProtocol::MapT, &customer_caps);
assert_eq!(map_t_config.protocol, TunnelProtocol::MapT);
assert_eq!(map_t_config.local_ipv4, Some("192.0.2.100".parse().unwrap()));
Ok(())
}
#[tokio::test]
async fn test_map_protocol_preference() -> Result<()> {
let isp_caps = create_isp_capabilities();
let map_e_config = create_tunnel_config(TunnelProtocol::MapE, &isp_caps);
let map_e_tunnel = create_tunnel(map_e_config)?;
assert_eq!(map_e_tunnel.protocol(), TunnelProtocol::MapE);
let map_t_config = create_tunnel_config(TunnelProtocol::MapT, &isp_caps);
let map_t_tunnel = create_tunnel(map_t_config)?;
assert_eq!(map_t_tunnel.protocol(), TunnelProtocol::MapT);
Ok(())
}
#[tokio::test]
async fn test_map_address_calculation_performance() -> Result<()> {
let config = create_test_map_e_config();
let tunnel = MapTunnel::new(config, MapProtocol::MapE)?;
let rule = create_sample_map_rule();
let start = std::time::Instant::now();
for i in 1..=255 {
let ipv4_addr = Ipv4Addr::new(192, 0, 2, i);
let _ipv6_addr = tunnel.calculate_ipv6_address(ipv4_addr, &rule)?;
}
let duration = start.elapsed();
println!("255 MAP address calculations took: {:?}", duration);
assert!(duration < std::time::Duration::from_millis(10));
Ok(())
}
#[tokio::test]
async fn test_map_psid_variations() -> Result<()> {
let config = create_test_map_e_config();
let tunnel = MapTunnel::new(config, MapProtocol::MapE)?;
for psid_length in [0, 2, 4, 6, 8] {
let rule = MapRule {
ipv6_prefix: "2001:db8::".parse().unwrap(),
ipv6_prefix_len: 32,
ipv4_prefix: "192.0.2.0".parse().unwrap(),
ipv4_prefix_len: 24,
port_params: PortParameters {
psid_offset: 4,
psid_length,
excluded_ports: 1024,
},
border_relay: Some("2001:db8:ffff::1".parse().unwrap()),
is_fmr: true,
};
let ipv4_addr: Ipv4Addr = "192.0.2.100".parse().unwrap();
let psid = tunnel.extract_psid(ipv4_addr, &rule);
let port_set = tunnel.calculate_port_set(psid, &rule);
println!("PSID length {}: PSID={}, ports={}",
psid_length, psid, port_set.port_count);
if psid_length > 0 {
assert!(psid < (1 << psid_length));
} else {
assert_eq!(psid, 0);
}
}
Ok(())
}