From 2c7879ad452b1c871a5b61afaeed2dde5c80f958 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sun, 11 Feb 2024 07:29:37 +0000 Subject: [PATCH] network: implement proper IPv6 networking --- container/src/init.rs | 8 ++++++-- network/examples/ping.rs | 2 +- network/src/backend.rs | 2 +- network/src/icmp.rs | 12 ++++++------ network/src/nat.rs | 32 ++++++++++++++++++++++++++++++++ network/src/proxynat/icmp.rs | 7 +++++-- 6 files changed, 51 insertions(+), 12 deletions(-) diff --git a/container/src/init.rs b/container/src/init.rs index d0a5ca9..6d043f5 100644 --- a/container/src/init.rs +++ b/container/src/init.rs @@ -350,7 +350,7 @@ impl ContainerInit { .await?; if ipv6_ready { - handle + let ipv6_gw_result = handle .route() .add() .v6() @@ -358,7 +358,11 @@ impl ContainerInit { .output_interface(link.header.index) .gateway(ipv6_gateway) .execute() - .await?; + .await; + + if let Err(error) = ipv6_gw_result { + warn!("failed to add ipv6 gateway route: {}", error); + } } Ok(()) diff --git a/network/examples/ping.rs b/network/examples/ping.rs index 55c21d9..c39426a 100644 --- a/network/examples/ping.rs +++ b/network/examples/ping.rs @@ -5,7 +5,7 @@ use hyphanet::icmp::{IcmpClient, IcmpProtocol}; #[tokio::main] async fn main() -> Result<()> { - let client = IcmpClient::new(IcmpProtocol::Icmp6)?; + let client = IcmpClient::new(IcmpProtocol::Icmpv6)?; let payload: [u8; 4] = [12u8, 14u8, 16u8, 32u8]; let result = client .ping6( diff --git a/network/src/backend.rs b/network/src/backend.rs index 2ae051b..0c297ea 100644 --- a/network/src/backend.rs +++ b/network/src/backend.rs @@ -114,7 +114,7 @@ impl NetworkBackend { let proxy = Box::new(ProxyNatHandlerFactory::new()); let ipv4 = IpCidr::from_str(&self.ipv4) .map_err(|_| anyhow!("failed to parse ipv4 cidr: {}", self.ipv4))?; - let ipv6 = IpCidr::from_str(&self.ipv4) + let ipv6 = IpCidr::from_str(&self.ipv6) .map_err(|_| anyhow!("failed to parse ipv6 cidr: {}", self.ipv6))?; let addresses: Vec = vec![ipv4, ipv6]; let mut kdev = diff --git a/network/src/icmp.rs b/network/src/icmp.rs index 4bb20c5..201ab5e 100644 --- a/network/src/icmp.rs +++ b/network/src/icmp.rs @@ -21,15 +21,15 @@ use tokio::{ #[derive(Debug)] pub enum IcmpProtocol { - Icmp4, - Icmp6, + Icmpv4, + Icmpv6, } impl IcmpProtocol { pub fn to_socket_protocol(&self) -> RawSocketProtocol { match self { - IcmpProtocol::Icmp4 => RawSocketProtocol::Icmpv4, - IcmpProtocol::Icmp6 => RawSocketProtocol::Icmpv6, + IcmpProtocol::Icmpv4 => RawSocketProtocol::Icmpv4, + IcmpProtocol::Icmpv6 => RawSocketProtocol::Icmpv6, } } } @@ -88,7 +88,7 @@ impl IcmpClient { let packet = &buffer[0..size]; let (token, reply) = match protocol { - IcmpProtocol::Icmp4 => { + IcmpProtocol::Icmpv4 => { let sliced = match SlicedPacket::from_ip(packet) { Ok(sliced) => sliced, Err(error) => { @@ -126,7 +126,7 @@ impl IcmpClient { (token, reply) } - IcmpProtocol::Icmp6 => { + IcmpProtocol::Icmpv6 => { let Ok(icmpv6) = Icmpv6Slice::from_slice(packet) else { continue; }; diff --git a/network/src/nat.rs b/network/src/nat.rs index a831c10..6e7003b 100644 --- a/network/src/nat.rs +++ b/network/src/nat.rs @@ -3,6 +3,8 @@ use async_trait::async_trait; use etherparse::Ethernet2Slice; use etherparse::Icmpv4Header; use etherparse::Icmpv4Type; +use etherparse::Icmpv6Header; +use etherparse::Icmpv6Type; use etherparse::IpNumber; use etherparse::IpPayloadSlice; use etherparse::Ipv4Slice; @@ -220,6 +222,11 @@ impl NatRouter { .await?; } + IpNumber::IPV6_ICMP => { + self.process_icmpv6(data, ether, source_addr, dest_addr, ipv6.payload()) + .await?; + } + _ => {} } @@ -295,6 +302,31 @@ impl NatRouter { Ok(()) } + pub async fn process_icmpv6<'a>( + &mut self, + data: &'a [u8], + ether: &Ethernet2Slice<'a>, + source_addr: IpAddress, + dest_addr: IpAddress, + payload: &IpPayloadSlice<'a>, + ) -> Result<()> { + let (header, _) = Icmpv6Header::from_slice(payload.payload)?; + let Icmpv6Type::EchoRequest(_) = header.icmp_type else { + return Ok(()); + }; + let source = IpEndpoint::new(source_addr, 0); + let dest = IpEndpoint::new(dest_addr, 0); + let key = NatKey { + protocol: NatKeyProtocol::Icmp, + client_mac: EthernetAddress(ether.source()), + local_mac: EthernetAddress(ether.destination()), + client_ip: source, + external_ip: dest, + }; + self.process_nat(data, key).await?; + Ok(()) + } + pub async fn process_nat(&mut self, data: &[u8], key: NatKey) -> Result<()> { for cidr in &self.local_cidrs { if cidr.contains_addr(&key.external_ip.addr) { diff --git a/network/src/proxynat/icmp.rs b/network/src/proxynat/icmp.rs index cbec0cc..7400c65 100644 --- a/network/src/proxynat/icmp.rs +++ b/network/src/proxynat/icmp.rs @@ -48,7 +48,10 @@ impl ProxyIcmpHandler { context: NatHandlerContext, rx_receiver: Receiver>, ) -> Result<()> { - let client = IcmpClient::new(IcmpProtocol::Icmp4)?; + let client = IcmpClient::new(match context.key.external_ip.addr { + IpAddress::Ipv4(_) => IcmpProtocol::Icmpv4, + IpAddress::Ipv6(_) => IcmpProtocol::Icmpv6, + })?; tokio::spawn(async move { if let Err(error) = ProxyIcmpHandler::process(client, rx_receiver, context).await { warn!("processing of icmp proxy failed: {}", error); @@ -157,7 +160,7 @@ impl ProxyIcmpHandler { ipv6: &Ipv6Slice<'_>, client: &IcmpClient, ) -> Result<()> { - if ipv6.header().next_header() != IpNumber::ICMP { + if ipv6.header().next_header() != IpNumber::IPV6_ICMP { return Ok(()); }