diff --git a/rust/libs/connlib/tunnel/src/gateway/client_on_gateway.rs b/rust/libs/connlib/tunnel/src/gateway/client_on_gateway.rs index c178ea9bb..7fa5ca624 100644 --- a/rust/libs/connlib/tunnel/src/gateway/client_on_gateway.rs +++ b/rust/libs/connlib/tunnel/src/gateway/client_on_gateway.rs @@ -300,6 +300,10 @@ impl ClientOnGateway { packet: IpPacket, now: Instant, ) -> anyhow::Result { + if packet.icmp_error().is_ok_and(|e| e.is_some()) { + bail!(UnroutablePacket::outbound_icmp_error(&packet)) + } + // Filtering a packet is not an error. if let Err(e) = self.ensure_allowed_outbound(&packet) { tracing::debug!(filtered_packet = ?packet, "{e:#}"); @@ -686,6 +690,7 @@ mod tests { time::Duration, }; + use anyhow::ErrorExt; use ip_packet::make::TcpFlags; use crate::{ @@ -1210,6 +1215,34 @@ mod tests { assert!(peer.permanent_translations.contains_key(&proxy_ip6_2())); } + #[test] + fn no_translate_outbound_icmp_error() { + let _guard = logging::test("trace"); + + let now = Instant::now(); + + let mut peer = ClientOnGateway::new( + client_id(), + client_tun(), + gateway_tun(), + flow_tracker::ClientProperties::default(), + ); + + let icmp_unreachable = ip_packet::make::icmp_dest_unreachable_network( + &ip_packet::make::udp_packet(proxy_ip4_1(), client_tun_ipv4(), 443, 50000, vec![]) + .unwrap(), + ) + .unwrap(); + + let error = peer.translate_outbound(icmp_unreachable, now).unwrap_err(); + let error = error.any_downcast_ref::().unwrap(); + + assert_eq!(error.to_string(), "Unroutable packet: OutboundIcmpError"); + assert_eq!(error.source().to_string(), "unknown"); + assert_eq!(error.destination().to_string(), "unknown"); + assert_eq!(error.proto().to_string(), "unknown"); + } + fn foo_dns_resource() -> crate::messages::gateway::ResourceDescription { crate::messages::gateway::ResourceDescription::Dns( crate::messages::gateway::ResourceDescriptionDns { diff --git a/rust/libs/connlib/tunnel/src/gateway/unroutable_packet.rs b/rust/libs/connlib/tunnel/src/gateway/unroutable_packet.rs index 11a6bda6e..7ca0012a5 100644 --- a/rust/libs/connlib/tunnel/src/gateway/unroutable_packet.rs +++ b/rust/libs/connlib/tunnel/src/gateway/unroutable_packet.rs @@ -48,6 +48,13 @@ impl UnroutablePacket { } } + pub fn outbound_icmp_error(packet: &IpPacket) -> Self { + Self { + five_tuple: FiveTuple::for_packet(packet), + error: RoutingError::OutboundIcmpError, + } + } + pub fn reason(&self) -> RoutingError { self.error } @@ -138,6 +145,8 @@ pub enum RoutingError { NoPeerState, #[display("No connection")] NotConnected, + #[display("OutboundIcmpError")] + OutboundIcmpError, #[display("Other")] Other, } @@ -150,6 +159,7 @@ impl From for opentelemetry::Value { RoutingError::NotAPeer => opentelemetry::Value::from("NotAPeer"), RoutingError::NoPeerState => opentelemetry::Value::from("NoPeerState"), RoutingError::NotConnected => opentelemetry::Value::from("NotConnected"), + RoutingError::OutboundIcmpError => opentelemetry::Value::from("OutboundIcmpError"), RoutingError::Other => opentelemetry::Value::from("Other"), } }