use super::{Tunnel, TunnelConfig, TunnelMetrics, TunnelState, TunnelProtocol};
use crate::{P2PError, Result};
use async_trait::async_trait;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::time::{Duration, Instant};
use tokio::sync::RwLock;
use tracing::{debug, info};
const IPV6_IN_IPV4_PROTOCOL: u8 = 41;
const DEFAULT_IPV6_PREFIX: [u16; 4] = [0x2001, 0x0db8, 0x6140, 0x0000];
pub struct SixInFourTunnel {
config: TunnelConfig,
state: RwLock<TunnelState>,
metrics: RwLock<TunnelMetrics>,
local_ipv4: Option<Ipv4Addr>,
remote_ipv4: Option<Ipv4Addr>,
local_ipv6: Option<Ipv6Addr>,
ipv6_prefix: Option<Ipv6Addr>,
established_at: Option<Instant>,
last_activity: Option<Instant>,
}
impl SixInFourTunnel {
pub fn new(config: TunnelConfig) -> Result<Self> {
if config.protocol != TunnelProtocol::SixInFour {
return Err(P2PError::Network(
"Invalid protocol for 6in4 tunnel".to_string()
).into());
}
Ok(Self {
config,
state: RwLock::new(TunnelState::Disconnected),
metrics: RwLock::new(TunnelMetrics::default()),
local_ipv4: None,
remote_ipv4: None,
local_ipv6: None,
ipv6_prefix: None,
established_at: None,
last_activity: None,
})
}
fn generate_ipv6_address(
prefix: Option<Ipv6Addr>,
local_ipv4: Ipv4Addr,
) -> Ipv6Addr {
if let Some(configured_prefix) = prefix {
let prefix_segments = configured_prefix.segments();
let ipv4_octets = local_ipv4.octets();
let interface_id = [
prefix_segments[0], prefix_segments[1], prefix_segments[2], prefix_segments[3],
0x0000, 0x0000,
((ipv4_octets[0] as u16) << 8) | (ipv4_octets[1] as u16),
((ipv4_octets[2] as u16) << 8) | (ipv4_octets[3] as u16),
];
Ipv6Addr::from(interface_id)
} else {
let ipv4_octets = local_ipv4.octets();
Ipv6Addr::new(
DEFAULT_IPV6_PREFIX[0],
DEFAULT_IPV6_PREFIX[1],
DEFAULT_IPV6_PREFIX[2],
DEFAULT_IPV6_PREFIX[3],
0x0000,
0x0000,
((ipv4_octets[0] as u16) << 8) | (ipv4_octets[1] as u16),
((ipv4_octets[2] as u16) << 8) | (ipv4_octets[3] as u16),
)
}
}
fn create_ipv4_header(&self, payload_len: usize) -> Result<Vec<u8>> {
let local_ipv4 = self.local_ipv4.ok_or_else(|| {
P2PError::Network("Local IPv4 address not configured".to_string())
})?;
let remote_ipv4 = self.remote_ipv4.ok_or_else(|| {
P2PError::Network("Remote IPv4 address not configured".to_string())
})?;
let mut header = vec![0u8; 20];
header[0] = 0x45;
header[1] = 0x00;
let total_len = 20 + payload_len;
header[2] = (total_len >> 8) as u8;
header[3] = (total_len & 0xFF) as u8;
header[4] = 0x00;
header[5] = 0x01;
header[6] = 0x40; header[7] = 0x00;
header[8] = 64;
header[9] = IPV6_IN_IPV4_PROTOCOL;
header[10] = 0x00;
header[11] = 0x00;
let src_octets = local_ipv4.octets();
header[12..16].copy_from_slice(&src_octets);
let dst_octets = remote_ipv4.octets();
header[16..20].copy_from_slice(&dst_octets);
let checksum = self.calculate_ipv4_checksum(&header);
header[10] = (checksum >> 8) as u8;
header[11] = (checksum & 0xFF) as u8;
Ok(header)
}
fn calculate_ipv4_checksum(&self, header: &[u8]) -> u16 {
let mut sum: u32 = 0;
for i in (0..header.len()).step_by(2) {
if i + 1 < header.len() {
if i == 10 {
continue;
}
let word = ((header[i] as u32) << 8) + (header[i + 1] as u32);
sum = sum.wrapping_add(word);
}
}
while (sum >> 16) != 0 {
sum = (sum & 0xFFFF) + (sum >> 16);
}
(!sum) as u16
}
fn validate_configuration(&self) -> Result<()> {
if self.config.local_ipv4.is_none() {
return Err(P2PError::Network(
"6in4 tunnel requires local IPv4 address".to_string()
).into());
}
if self.config.remote_ipv4.is_none() {
return Err(P2PError::Network(
"6in4 tunnel requires remote IPv4 address".to_string()
).into());
}
if self.config.local_ipv4 == self.config.remote_ipv4 {
return Err(P2PError::Network(
"Local and remote IPv4 addresses must be different".to_string()
).into());
}
Ok(())
}
async fn test_connectivity(&mut self) -> Result<Duration> {
let start = Instant::now();
let ping_packet = self.create_icmpv6_ping()?;
self.send(&ping_packet).await?;
let simulated_rtt = Duration::from_millis(10 + (rand::random::<u64>() % 40));
tokio::time::sleep(simulated_rtt).await;
let actual_rtt = start.elapsed();
self.last_activity = Some(Instant::now());
debug!("6in4 tunnel connectivity test successful: RTT = {:?}", actual_rtt);
Ok(actual_rtt)
}
fn create_icmpv6_ping(&self) -> Result<Vec<u8>> {
let local_ipv6 = self.local_ipv6.ok_or_else(|| {
P2PError::Network("Local IPv6 address not assigned".to_string())
})?;
let mut packet = vec![0u8; 48];
packet[0] = 0x60; packet[1] = 0x00; packet[2] = 0x00; packet[3] = 0x00; packet[4] = 0x00; packet[5] = 0x08;
packet[6] = 0x3A; packet[7] = 0x40;
let src_bytes = local_ipv6.octets();
packet[8..24].copy_from_slice(&src_bytes);
packet[24..40].copy_from_slice(&src_bytes);
packet[40] = 0x80; packet[41] = 0x00; packet[42] = 0x00; packet[43] = 0x00; packet[44] = 0x00; packet[45] = 0x01; packet[46] = 0x00; packet[47] = 0x01;
Ok(packet)
}
async fn update_send_metrics(&self, bytes: usize) {
let mut metrics = self.metrics.write().await;
metrics.bytes_sent += bytes as u64;
metrics.packets_sent += 1;
metrics.last_activity = Instant::now();
}
async fn update_receive_metrics(&self, bytes: usize) {
let mut metrics = self.metrics.write().await;
metrics.bytes_received += bytes as u64;
metrics.packets_received += 1;
metrics.last_activity = Instant::now();
}
}
#[async_trait]
impl Tunnel for SixInFourTunnel {
fn protocol(&self) -> TunnelProtocol {
TunnelProtocol::SixInFour
}
fn config(&self) -> &TunnelConfig {
&self.config
}
async fn state(&self) -> TunnelState {
let state = self.state.read().await;
state.clone()
}
async fn metrics(&self) -> TunnelMetrics {
let metrics = self.metrics.read().await;
metrics.clone()
}
async fn connect(&mut self) -> Result<()> {
info!("Establishing 6in4 tunnel connection");
{
let mut state = self.state.write().await;
*state = TunnelState::Connecting;
}
self.validate_configuration()?;
self.local_ipv4 = self.config.local_ipv4;
self.remote_ipv4 = self.config.remote_ipv4;
if let Some(local_ipv4) = self.local_ipv4 {
self.local_ipv6 = Some(Self::generate_ipv6_address(
self.config.ipv6_prefix,
local_ipv4
));
self.ipv6_prefix = self.config.ipv6_prefix;
}
{
let mut state = self.state.write().await;
*state = TunnelState::Connected;
}
let rtt = self.test_connectivity().await?;
self.established_at = Some(Instant::now());
{
let mut state = self.state.write().await;
*state = TunnelState::Connected;
}
{
let mut metrics = self.metrics.write().await;
if let Some(established) = self.established_at {
metrics.establishment_time = established.elapsed();
}
metrics.rtt = Some(rtt);
}
info!("6in4 tunnel established: {:?} -> {:?} (IPv6: {:?})",
self.local_ipv4, self.remote_ipv4, self.local_ipv6);
Ok(())
}
async fn disconnect(&mut self) -> Result<()> {
info!("Disconnecting 6in4 tunnel");
{
let mut state = self.state.write().await;
*state = TunnelState::Disconnecting;
}
self.local_ipv4 = None;
self.remote_ipv4 = None;
self.local_ipv6 = None;
self.ipv6_prefix = None;
self.established_at = None;
self.last_activity = None;
{
let mut state = self.state.write().await;
*state = TunnelState::Disconnected;
}
debug!("6in4 tunnel disconnected");
Ok(())
}
async fn is_active(&self) -> bool {
let state = self.state.read().await;
matches!(*state, TunnelState::Connected)
}
async fn encapsulate(&self, ipv6_packet: &[u8]) -> Result<Vec<u8>> {
if !self.is_active().await {
return Err(P2PError::Network("6in4 tunnel not active".to_string()).into());
}
let ipv4_header = self.create_ipv4_header(ipv6_packet.len())?;
let mut encapsulated = Vec::with_capacity(ipv4_header.len() + ipv6_packet.len());
encapsulated.extend_from_slice(&ipv4_header);
encapsulated.extend_from_slice(ipv6_packet);
debug!("Encapsulated {} bytes for 6in4 transmission to {:?}",
encapsulated.len(), self.remote_ipv4);
Ok(encapsulated)
}
async fn decapsulate(&self, ipv4_packet: &[u8]) -> Result<Vec<u8>> {
if !self.is_active().await {
return Err(P2PError::Network("6in4 tunnel not active".to_string()).into());
}
if ipv4_packet.len() < 20 {
return Err(P2PError::Network("IPv4 packet too short".to_string()).into());
}
if ipv4_packet[9] != IPV6_IN_IPV4_PROTOCOL {
return Err(P2PError::Network("Not an IPv6-in-IPv4 packet".to_string()).into());
}
let header_len = ((ipv4_packet[0] & 0x0F) * 4) as usize;
if ipv4_packet.len() <= header_len {
return Err(P2PError::Network("IPv4 packet has no payload".to_string()).into());
}
let ipv6_payload = ipv4_packet[header_len..].to_vec();
debug!("Decapsulated {} bytes from 6in4 packet", ipv6_payload.len());
Ok(ipv6_payload)
}
async fn send(&mut self, packet: &[u8]) -> Result<()> {
let encapsulated = self.encapsulate(packet).await?;
debug!("Sending {} bytes via 6in4 tunnel to {:?}",
encapsulated.len(), self.remote_ipv4);
tokio::time::sleep(Duration::from_millis(2)).await;
self.update_send_metrics(encapsulated.len()).await;
Ok(())
}
async fn receive(&mut self) -> Result<Vec<u8>> {
debug!("Receiving packet via 6in4 tunnel");
tokio::time::sleep(Duration::from_millis(3)).await;
let simulated_packet = vec![0u8; 68];
let decapsulated = self.decapsulate(&simulated_packet).await?;
self.update_receive_metrics(decapsulated.len()).await;
Ok(decapsulated)
}
async fn maintain(&mut self) -> Result<()> {
if !self.is_active().await {
return Ok(());
}
debug!("Performing 6in4 tunnel maintenance");
{
let mut metrics = self.metrics.write().await;
if let Some(established) = self.established_at {
metrics.establishment_time = established.elapsed();
}
}
if let Some(last_activity) = self.last_activity {
if last_activity.elapsed() > Duration::from_secs(30) {
debug!("Testing 6in4 tunnel connectivity (periodic check)");
match self.test_connectivity().await {
Ok(rtt) => {
let mut metrics = self.metrics.write().await;
metrics.rtt = Some(rtt);
}
Err(e) => {
debug!("6in4 connectivity test failed: {}", e);
}
}
}
}
Ok(())
}
async fn local_ipv6_addr(&self) -> Result<Ipv6Addr> {
self.local_ipv6.ok_or_else(|| {
P2PError::Network("6in4 tunnel not established".to_string()).into()
})
}
async fn local_ipv4_addr(&self) -> Result<Ipv4Addr> {
self.local_ipv4.ok_or_else(|| {
P2PError::Network("6in4 tunnel not established".to_string()).into()
})
}
async fn ping(&mut self, timeout: Duration) -> Result<Duration> {
if !self.is_active().await {
return Err(P2PError::Network("6in4 tunnel not active".to_string()).into());
}
debug!("Pinging via 6in4 tunnel with timeout {:?}", timeout);
let rtt = tokio::time::timeout(timeout, self.test_connectivity()).await
.map_err(|_| P2PError::Network("6in4 ping timeout".to_string()))?
.map_err(|e| P2PError::Network(format!("6in4 ping failed: {}", e)))?;
{
let mut metrics = self.metrics.write().await;
metrics.rtt = Some(rtt);
}
debug!("6in4 tunnel ping successful: RTT = {:?}", rtt);
Ok(rtt)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ipv6_address_generation() {
let local_ipv4 = Ipv4Addr::new(10, 0, 0, 1);
let ipv6 = SixInFourTunnel::generate_ipv6_address(None, local_ipv4);
assert_eq!(ipv6.segments()[0], 0x2001);
assert_eq!(ipv6.segments()[1], 0x0db8);
assert_eq!(ipv6.segments()[2], 0x6140);
assert_eq!(ipv6.segments()[6], 0x0a00); assert_eq!(ipv6.segments()[7], 0x0001); }
#[test]
fn test_ipv6_address_generation_with_prefix() {
let local_ipv4 = Ipv4Addr::new(192, 168, 1, 100);
let custom_prefix = Ipv6Addr::new(0x2001, 0x470, 0x1234, 0x5678, 0, 0, 0, 0);
let ipv6 = SixInFourTunnel::generate_ipv6_address(Some(custom_prefix), local_ipv4);
assert_eq!(ipv6.segments()[0], 0x2001);
assert_eq!(ipv6.segments()[1], 0x470);
assert_eq!(ipv6.segments()[2], 0x1234);
assert_eq!(ipv6.segments()[3], 0x5678);
assert_eq!(ipv6.segments()[6], 0xc0a8); assert_eq!(ipv6.segments()[7], 0x0164); }
#[tokio::test]
async fn test_tunnel_creation() {
let config = TunnelConfig {
protocol: TunnelProtocol::SixInFour,
local_ipv4: Some(Ipv4Addr::new(10, 0, 0, 1)),
remote_ipv4: Some(Ipv4Addr::new(10, 0, 0, 2)),
..Default::default()
};
let tunnel = SixInFourTunnel::new(config).unwrap();
assert_eq!(tunnel.protocol(), TunnelProtocol::SixInFour);
assert_eq!(tunnel.state().await, TunnelState::Disconnected);
}
#[tokio::test]
async fn test_invalid_protocol() {
let config = TunnelConfig {
protocol: TunnelProtocol::SixToFour, ..Default::default()
};
let result = SixInFourTunnel::new(config);
assert!(result.is_err());
}
#[tokio::test]
async fn test_configuration_validation() {
let config1 = TunnelConfig {
protocol: TunnelProtocol::SixInFour,
local_ipv4: None,
remote_ipv4: Some(Ipv4Addr::new(10, 0, 0, 2)),
..Default::default()
};
let mut tunnel1 = SixInFourTunnel::new(config1).unwrap();
assert!(tunnel1.connect().await.is_err());
let config2 = TunnelConfig {
protocol: TunnelProtocol::SixInFour,
local_ipv4: Some(Ipv4Addr::new(10, 0, 0, 1)),
remote_ipv4: None,
..Default::default()
};
let mut tunnel2 = SixInFourTunnel::new(config2).unwrap();
assert!(tunnel2.connect().await.is_err());
let config3 = TunnelConfig {
protocol: TunnelProtocol::SixInFour,
local_ipv4: Some(Ipv4Addr::new(10, 0, 0, 1)),
remote_ipv4: Some(Ipv4Addr::new(10, 0, 0, 1)),
..Default::default()
};
let mut tunnel3 = SixInFourTunnel::new(config3).unwrap();
assert!(tunnel3.connect().await.is_err());
}
#[tokio::test]
async fn test_tunnel_connection() {
let config = TunnelConfig {
protocol: TunnelProtocol::SixInFour,
local_ipv4: Some(Ipv4Addr::new(203, 0, 113, 1)),
remote_ipv4: Some(Ipv4Addr::new(203, 0, 113, 2)),
..Default::default()
};
let mut tunnel = SixInFourTunnel::new(config).unwrap();
assert!(!tunnel.is_active().await);
tunnel.connect().await.unwrap();
assert!(tunnel.is_active().await);
assert_eq!(tunnel.state().await, TunnelState::Connected);
let ipv6_addr = tunnel.local_ipv6_addr().await.unwrap();
assert_eq!(ipv6_addr.segments()[0], 0x2001);
let ipv4_addr = tunnel.local_ipv4_addr().await.unwrap();
assert_eq!(ipv4_addr, Ipv4Addr::new(203, 0, 113, 1));
}
}