diff --git a/nat/src/masquerade/allocation.rs b/nat/src/masquerade/allocation.rs index aace14e208..da40ac1ffb 100644 --- a/nat/src/masquerade/allocation.rs +++ b/nat/src/masquerade/allocation.rs @@ -22,7 +22,7 @@ pub enum AllocatorError { #[error("failed to reserve port: {0}")] PortReservationFailed(u16), #[error("unsupported protocol: {0:?}")] - UnsupportedProtocol(NextHeader), + UnsupportedProtocol(NextHeader), // FIXME: remove this when possible #[error("missing VPC discriminant")] MissingDiscriminant, // Something has gone wrong, but user input or packet input are not responsible. diff --git a/nat/src/masquerade/apalloc/mod.rs b/nat/src/masquerade/apalloc/mod.rs index 67ee1460d6..8343dbe756 100644 --- a/nat/src/masquerade/apalloc/mod.rs +++ b/nat/src/masquerade/apalloc/mod.rs @@ -427,12 +427,7 @@ impl NatAllocator { }), } } - fn check_proto(next_header: NextHeader) -> Result<(), AllocatorError> { - match next_header { - NextHeader::TCP | NextHeader::UDP | NextHeader::ICMP | NextHeader::ICMP6 => Ok(()), - _ => Err(AllocatorError::UnsupportedProtocol(next_header)), - } - } + fn allocate_from_tables( src_ip: IpAddr, src_vpcd: VpcDiscriminant, @@ -440,7 +435,8 @@ impl NatAllocator { next_header: NextHeader, pools_src: &PoolTable, ) -> Result>, AllocatorError> { - Self::check_proto(next_header)?; + // TODO: here we should only allow next-header to be TCP/UDP/ICMP/ICMP6 as a SANITY. + // This can be done by a transparent wrapper of NextHeader that can only exist for that set // If we could not find an address pool for the source address, the user has not exposed // and configured NAT for that source address. Drop the packet instead of creating a session. diff --git a/nat/src/masquerade/nf.rs b/nat/src/masquerade/nf.rs index e7e6137afd..3edc15fb2f 100644 --- a/nat/src/masquerade/nf.rs +++ b/nat/src/masquerade/nf.rs @@ -20,7 +20,7 @@ use net::buffer::PacketBufferMut; use net::flow_key::IcmpProtoKey; use net::flows::{ExtractRef, FlowInfo, FlowInfoError}; use net::headers::{TryIp, TryTcp}; -use net::ip::UnicastIpAddr; +use net::ip::{NextHeader, UnicastIpAddr}; use net::packet::{DoneReason, Packet, VpcDiscriminant}; use net::{FlowKey, IpProtoKey}; use pipeline::{NetworkFunction, PipelineData}; @@ -61,6 +61,8 @@ pub(crate) enum MasqueradeError { NatError(#[from] NatPacketError), #[error("Failed to create flow state: {0}")] FlowError(#[from] FlowInfoError), + #[error("unsupported protocol: {0:?}")] + UnsupportedProtocol(NextHeader), } /// A stateful NAT processor, implementing the [`NetworkFunction`] trait. [`Masquerade`] processes @@ -370,6 +372,14 @@ impl Masquerade { )) } + /// Tell if a protocol can be masqueraded + fn can_be_masqueraded(next_header: NextHeader) -> bool { + matches!( + next_header, + NextHeader::TCP | NextHeader::UDP | NextHeader::ICMP | NextHeader::ICMP6 + ) + } + /// Main entry point for masquerading logic fn masquerade_packet( &self, @@ -410,14 +420,24 @@ impl Masquerade { .copied() .unwrap_or(current_flow_key); - // Create a new session and translate the address - let src_ip = *initial_flow_key.src_ip(); - let alloc = allocator - .allocate(src_vpcd, dst_vpcd, src_ip, initial_flow_key.proto()) - .map_err(MasqueradeError::AllocationFailure)?; + // check if the flow can be masqueraded + let proto = initial_flow_key.proto(); + if !Self::can_be_masqueraded(proto) { + return Err(MasqueradeError::UnsupportedProtocol(proto)); + } - // The generation the installed allocator serves - let genid = allocator.genid(); + // allocate an ip and port for this flow + let src_ip = *initial_flow_key.src_ip(); + let alloc = match allocator.allocate(src_vpcd, dst_vpcd, src_ip, proto) { + Ok(alloc) => alloc, + Err(e) => { + warn!( + "{nfi}: Ip/port allocation failed for flow {initial_flow_key} towards VPC {dst_vpcd}: {e}" + ); + return Err(MasqueradeError::AllocationFailure(e)); + } + }; + debug!("{nfi}: Allocated: {alloc}"); // Forbid addresses we won't know how to translate. This is a work around of a larger change if let Err(addr) = UnicastIpAddr::try_from(alloc.allocation.ip()) { @@ -425,7 +445,8 @@ impl Masquerade { return Err(MasqueradeError::Bug("allocated unusable ip")); } - debug!("{nfi}: Allocated: {alloc}"); + // The generation the installed allocator serves + let genid = allocator.genid(); // create flow pair let installed = @@ -526,7 +547,9 @@ impl Masquerade { impl From<&MasqueradeError> for DoneReason { fn from(error: &MasqueradeError) -> Self { match error { - MasqueradeError::BadTransportHeader => DoneReason::NatUnsupportedProto, + MasqueradeError::BadTransportHeader | MasqueradeError::UnsupportedProtocol(_) => { + DoneReason::NatUnsupportedProto + } MasqueradeError::FlowKeyError | MasqueradeError::InvalidPort(_) => { DoneReason::Malformed }