diff --git a/Cargo.lock b/Cargo.lock index 0817c1e..82bf947 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,9 +10,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -21,13 +21,13 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.144" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "nat_emulation" -version = "0.0.2" +version = "1.0.0" dependencies = [ "rand", ] diff --git a/Cargo.toml b/Cargo.toml index e645461..f5a4c66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nat_emulation" -version = "0.0.2" +version = "1.0.0" authors = ["Monica Moniot"] edition = "2021" diff --git a/src/lib.rs b/src/lib.rs index 20bf1d7..4d63400 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ +#[warn(missing_docs)] mod nat_flags; -pub use nat_flags::{flags, predefines}; +pub use nat_flags::{flags, port_ranges, predefines}; mod nat; pub use nat::{DestType, Nat}; @@ -18,7 +19,7 @@ mod examples { let client_port = 17; let server_addr = 22222; let server_port = 80; - let mut firewall = Nat::no_address_translation(STATEFUL_FIREWALL, client_addr, rng, timeout); + let mut firewall = Nat::no_address_translation(STATEFUL_FIREWALL, client_addr, rng, usize::MAX, timeout); assert_eq!(firewall.assign_internal_address(), client_addr); time += 100; @@ -60,7 +61,7 @@ mod examples { let server0_addr = 22222; let server1_addr = 33333; let server_port = 80; - let mut firewall = Nat::no_address_translation(RESTRICTED_FIREWALL, client_addr, rng, timeout); + let mut firewall = Nat::no_address_translation(RESTRICTED_FIREWALL, client_addr, rng, usize::MAX, timeout); assert_eq!(firewall.assign_internal_address(), client_addr); time += 100; @@ -85,7 +86,7 @@ mod examples { let server_addr = 22222; let server0_port = 80; let server1_port = 17; - let mut firewall = Nat::no_address_translation(PORT_RESTRICTED_FIREWALL, client_addr, rng, timeout); + let mut firewall = Nat::no_address_translation(PORT_RESTRICTED_FIREWALL, client_addr, rng, usize::MAX, timeout); assert_eq!(firewall.assign_internal_address(), client_addr); @@ -100,15 +101,15 @@ mod examples { #[test] fn easy_nat() { use nat_emulation::predefines::EASY_NAT; - use nat_emulation::{DestType, Nat}; + use nat_emulation::{port_ranges::PRIVATE, DestType, Nat}; let rng = rand::rngs::mock::StepRng::new(0, 1); let mut time = 100; let timeout = 1000 * 60 * 2; let nat_ex_addr = 11111; - let mut nat = Nat::new(EASY_NAT, [nat_ex_addr], 90000..=99999, 49152..=u16::MAX, rng, timeout); + let mut nat = Nat::new(EASY_NAT, [nat_ex_addr], 90000..=99999, PRIVATE, rng, usize::MAX, timeout); let client_in_addr = nat.assign_internal_address(); - let client_in_port = 17; + let client_in_port = 25565; let server_ex_addr = 22222; let server_ex_port = 80; @@ -143,15 +144,15 @@ mod examples { #[test] fn full_cone_nat() { use nat_emulation::predefines::FULL_CONE_NAT; - use nat_emulation::{DestType, Nat}; + use nat_emulation::{port_ranges::PRIVATE, DestType, Nat}; let rng = rand::rngs::mock::StepRng::new(0, 1); let mut time = 100; let timeout = 1000 * 60 * 2; let nat_ex_addr = 11111; - let mut nat = Nat::new(FULL_CONE_NAT, [nat_ex_addr], 90000..=99999, 49152..=u16::MAX, rng, timeout); + let mut nat = Nat::new(FULL_CONE_NAT, [nat_ex_addr], 90000..=99999, PRIVATE, rng, usize::MAX, timeout); let client_in_addr = nat.assign_internal_address(); - let client_in_port = 17; + let client_in_port = 25565; let server_ex_addr = 22222; let server_ex_port = 80; @@ -174,15 +175,15 @@ mod examples { #[test] fn symmetric_nat() { use nat_emulation::predefines::SYMMETRIC_NAT; - use nat_emulation::{DestType::*, Nat}; + use nat_emulation::{port_ranges::PRIVATE, DestType::*, Nat}; let rng = rand::rngs::mock::StepRng::new(0, 1); let mut time = 100; let timeout = 1000 * 60 * 2; let nat_ex_addr = 11111; - let mut nat = Nat::new(SYMMETRIC_NAT, [nat_ex_addr], 90000..=99999, 49152..=u16::MAX, rng, timeout); + let mut nat = Nat::new(SYMMETRIC_NAT, [nat_ex_addr], 90000..=99999, PRIVATE, rng, usize::MAX, timeout); let client_in_addr = nat.assign_internal_address(); - let client_in_port = 17; + let client_in_port = 25565; let server_ex_addr = 22222; let server_ex_port0 = 80; let server_ex_port1 = 17; @@ -215,14 +216,14 @@ mod examples { #[test] fn hard_nat() { use nat_emulation::predefines::HARD_NAT; - use nat_emulation::{DestType::*, Nat}; + use nat_emulation::{port_ranges::PRIVATE, DestType::*, Nat}; let rng = rand::rngs::mock::StepRng::new(0, 1); let mut time = 100; let timeout = 1000 * 60 * 2; - let mut nat = Nat::new(HARD_NAT, [11110, 11111, 11112, 11113], 90000..=99999, 49152..=u16::MAX, rng, timeout); + let mut nat = Nat::new(HARD_NAT, [11110, 11111, 11112, 11113], 90000..=99999, PRIVATE, rng, usize::MAX, timeout); let client_in_addr = nat.assign_internal_address(); - let client_in_port = 17; + let client_in_port = 25565; let server_ex_addr = 22222; let server_ex_port0 = 80; let server_ex_port1 = 17; @@ -263,16 +264,16 @@ mod examples { #[test] fn misbehaving_nat() { use nat_emulation::predefines::MISBEHAVING_NAT; - use nat_emulation::{DestType, Nat}; + use nat_emulation::{port_ranges::PRIVATE, DestType, Nat}; let rng = rand::rngs::mock::StepRng::new(0, 1); let mut time = 100; let timeout = 1000 * 60 * 2; let nat_ex_addr = 11111; - let mut nat = Nat::new(MISBEHAVING_NAT, [nat_ex_addr], 90000..=99999, 49152..=u16::MAX, rng, timeout); + let mut nat = Nat::new(MISBEHAVING_NAT, [nat_ex_addr], 90000..=99999, PRIVATE, rng, usize::MAX, timeout); let client_in_addr = nat.assign_internal_address(); - let client_in_port = 17; + let client_in_port = 25565; let server_ex_addr = 22222; let server_ex_port0 = 80; let server_ex_port1 = 17; diff --git a/src/nat.rs b/src/nat.rs index bcf911f..d7c777b 100644 --- a/src/nat.rs +++ b/src/nat.rs @@ -4,6 +4,7 @@ use std::ops::RangeInclusive; use rand::RngCore; use crate::flags::*; +use crate::nat_flags::port_ranges; pub enum DestType { External { @@ -58,6 +59,8 @@ pub struct Nat { rng: R, assigned_external_ports: RangeInclusive, assigned_internal_addresses: RangeInclusive, + map_cur_size: usize, + map_max_size: usize, /// This field defines the set of behaviors this NAT will exhibit. /// Some NATs will dynamically change their behavior during runtime in response to arbitrary /// triggers. This classified as a Non-deterministic NAT by rfc4787, and it is awful. @@ -76,13 +79,14 @@ impl Nat { /// firewall. It can still translate ports however, unless you disable this behavior as well /// with the `PORT_PRESERVATION_OVERRIDE` flag. #[inline] - pub fn no_address_translation(flags: u32, assigned_address: u32, rng: R, mapping_timeout: i64) -> Self { + pub fn no_address_translation(flags: u32, assigned_address: u32, rng: R, mapping_max_size: usize, mapping_timeout: i64) -> Self { Self::new( flags, [assigned_address], assigned_address..=assigned_address, - 0..=u16::MAX, + port_ranges::ALL, rng, + mapping_max_size, mapping_timeout, ) } @@ -97,6 +101,7 @@ impl Nat { internal_addresses: RangeInclusive, external_dynamic_ports: RangeInclusive, rng: R, + mapping_max_size: usize, mapping_timeout: i64, ) -> Self { debug_assert!( @@ -111,6 +116,7 @@ impl Nat { internal_addresses, external_dynamic_ports, rng, + mapping_max_size, mapping_timeout, ); ret.external_addresses_len = external_addresses.len(); @@ -135,8 +141,10 @@ impl Nat { internal_addresses: RangeInclusive, external_dynamic_ports: RangeInclusive, rng: R, + mapping_max_size: usize, mapping_timeout: i64, ) -> Self { + debug_assert!(mapping_max_size > 0, "The mapping max size must be greateer than 0"); debug_assert!( internal_addresses.start() <= internal_addresses.end(), "The internal_addresses range must be nonempty" @@ -149,6 +157,8 @@ impl Nat { external_addresses_len: M, external_addresses: external_addresses, map: std::array::from_fn(|_| Vec::new()), + map_cur_size: 0, + map_max_size: usize::MAX, mapping_timeout, rng, assigned_external_ports: external_dynamic_ports, @@ -268,6 +278,7 @@ impl Nat { if routing_table[i].external_port == src_port { // In port preservation override mode we remove everyone else who is // using the chosen src_port. + self.map_cur_size -= 1; routing_table.swap_remove(i); } } @@ -277,17 +288,22 @@ impl Nat { // If we can't do any port preservation we have to randomly generate the port and address let mut random_addr; let mut random_port; - let mut attempt_until_force = 8; + let mut attempt_until_force = 32; 'regen: loop { attempt_until_force -= 1; random_addr = paired_addr_idx.unwrap_or_else(|| { if M == 1 { 0 } else { - self.rng.next_u64() as usize % self.external_addresses_len + (self.rng.next_u32() % self.external_addresses_len as u32) as usize } }); - random_port = (self.rng.next_u32() % self.assigned_external_ports.len() as u32) as u16 + self.assigned_external_ports.start(); + let range = if self.flags & NO_WELL_KNOWN_PRESERVATION == 0 && port_ranges::WELL_KNOWN.contains(&src_port) { + &port_ranges::WELL_KNOWN + } else { + &self.assigned_external_ports + }; + random_port = (self.rng.next_u32() % range.len() as u32) as u16 + range.start(); if self.flags & NO_PORT_PARITY == 0 { // Force the port to have the same parity as the src_port. random_port = (random_port & !1u16) | (src_port & 1u16); @@ -299,6 +315,7 @@ impl Nat { continue 'regen; } // Remove this mapping so our random port is unique. + self.map_cur_size -= 1; routing_table.swap_remove(i); break; } @@ -361,6 +378,7 @@ impl Nat { while i < routing_table.len() { let route = &mut routing_table[i]; if route.last_used_time < expiry { + self.map_cur_size -= 1; routing_table.swap_remove(i); continue; } else if route.internal_addr == internal_src_addr && route.internal_port == internal_src_port { @@ -399,6 +417,20 @@ impl Nat { } }; let external_addr = self.external_addresses[external_address_idx]; + while self.map_cur_size >= self.map_max_size { + let idx = if M == 1 { + 0 + } else { + (self.rng.next_u32() % self.external_addresses_len as u32) as usize + }; + let routing_table = &mut self.map[idx]; + if !routing_table.is_empty() { + let idx = (self.rng.next_u32() % routing_table.len() as u32) as usize; + self.map_cur_size -= 1; + routing_table.swap_remove(idx); + } + } + self.map_cur_size += 1; self.map[external_address_idx].push(Entry { internal_addr: internal_src_addr, internal_port: internal_src_port, @@ -463,6 +495,7 @@ impl Nat { while i < routing_table.len() { let route = &mut routing_table[i]; if route.last_used_time < expiry { + self.map_cur_size -= 1; routing_table.swap_remove(i); continue; } else if route.external_port == external_dest_port { @@ -486,6 +519,7 @@ impl Nat { while i < routing_table.len() { let route = &routing_table[i]; if route.external_port == external_dest_port { + self.map_cur_size -= 1; routing_table.swap_remove(i); } else { i += 1; diff --git a/src/nat_flags.rs b/src/nat_flags.rs index d7c7747..0bfd3af 100644 --- a/src/nat_flags.rs +++ b/src/nat_flags.rs @@ -95,6 +95,10 @@ pub mod flags { /// /// This flag has no effect if `NO_PORT_PRESERVATION` is true. pub const PORT_PRESERVATION_OVERLOAD: u32 = 1 << 13; + /// By default, if a source port number is in the "well-known" port range, then the NAT will + /// attempt to generate a source port which is also in this range. + /// If true, the NAT will not do this. + pub const NO_WELL_KNOWN_PRESERVATION: u32 = 1 << 14; } /// This is a set of pre-defined flags for common NAT types. Each constant represents some /// common NAT or firewall types one might want to emulate with this library. These are provided for @@ -116,7 +120,7 @@ pub mod predefines { /// let client_port = 17; /// let server_addr = 22222; /// let server_port = 80; - /// let mut firewall = Nat::no_address_translation(STATEFUL_FIREWALL, client_addr, rng, timeout); + /// let mut firewall = Nat::no_address_translation(STATEFUL_FIREWALL, client_addr, rng, usize::MAX, timeout); /// assert_eq!(firewall.assign_internal_address(), client_addr); /// /// time += 100; @@ -158,7 +162,7 @@ pub mod predefines { /// let server0_addr = 22222; /// let server1_addr = 33333; /// let server_port = 80; - /// let mut firewall = Nat::no_address_translation(RESTRICTED_FIREWALL, client_addr, rng, timeout); + /// let mut firewall = Nat::no_address_translation(RESTRICTED_FIREWALL, client_addr, rng, usize::MAX, timeout); /// assert_eq!(firewall.assign_internal_address(), client_addr); /// /// time += 100; @@ -185,7 +189,7 @@ pub mod predefines { /// let server_addr = 22222; /// let server0_port = 80; /// let server1_port = 17; - /// let mut firewall = Nat::no_address_translation(PORT_RESTRICTED_FIREWALL, client_addr, rng, timeout); + /// let mut firewall = Nat::no_address_translation(PORT_RESTRICTED_FIREWALL, client_addr, rng, usize::MAX, timeout); /// /// assert_eq!(firewall.assign_internal_address(), client_addr); /// @@ -206,15 +210,15 @@ pub mod predefines { /// # Example /// ``` /// use nat_emulation::predefines::EASY_NAT; - /// use nat_emulation::{DestType, Nat}; + /// use nat_emulation::{port_ranges::PRIVATE, DestType, Nat}; /// let rng = rand::rngs::mock::StepRng::new(0, 1); /// let mut time = 100; /// let timeout = 1000 * 60 * 2; /// /// let nat_ex_addr = 11111; - /// let mut nat = Nat::new(EASY_NAT, [nat_ex_addr], 90000..=99999, 49152..=u16::MAX, rng, timeout); + /// let mut nat = Nat::new(EASY_NAT, [nat_ex_addr], 90000..=99999, PRIVATE, rng, usize::MAX, timeout); /// let client_in_addr = nat.assign_internal_address(); - /// let client_in_port = 17; + /// let client_in_port = 25565; /// let server_ex_addr = 22222; /// let server_ex_port = 80; /// @@ -253,15 +257,15 @@ pub mod predefines { /// # Example /// ``` /// use nat_emulation::predefines::FULL_CONE_NAT; - /// use nat_emulation::{DestType, Nat}; + /// use nat_emulation::{port_ranges::PRIVATE, DestType, Nat}; /// let rng = rand::rngs::mock::StepRng::new(0, 1); /// let mut time = 100; /// let timeout = 1000 * 60 * 2; /// /// let nat_ex_addr = 11111; - /// let mut nat = Nat::new(FULL_CONE_NAT, [nat_ex_addr], 90000..=99999, 49152..=u16::MAX, rng, timeout); + /// let mut nat = Nat::new(FULL_CONE_NAT, [nat_ex_addr], 90000..=99999, PRIVATE, rng, usize::MAX, timeout); /// let client_in_addr = nat.assign_internal_address(); - /// let client_in_port = 17; + /// let client_in_port = 25565; /// let server_ex_addr = 22222; /// let server_ex_port = 80; /// @@ -291,15 +295,15 @@ pub mod predefines { /// # Example /// ``` /// use nat_emulation::predefines::SYMMETRIC_NAT; - /// use nat_emulation::{DestType::*, Nat}; + /// use nat_emulation::{port_ranges::PRIVATE, DestType::*, Nat}; /// let rng = rand::rngs::mock::StepRng::new(0, 1); /// let mut time = 100; /// let timeout = 1000 * 60 * 2; /// /// let nat_ex_addr = 11111; - /// let mut nat = Nat::new(SYMMETRIC_NAT, [nat_ex_addr], 90000..=99999, 49152..=u16::MAX, rng, timeout); + /// let mut nat = Nat::new(SYMMETRIC_NAT, [nat_ex_addr], 90000..=99999, PRIVATE, rng, usize::MAX, timeout); /// let client_in_addr = nat.assign_internal_address(); - /// let client_in_port = 17; + /// let client_in_port = 25565; /// let server_ex_addr = 22222; /// let server_ex_port0 = 80; /// let server_ex_port1 = 17; @@ -331,18 +335,18 @@ pub mod predefines { /// ``` pub const SYMMETRIC_NAT: u32 = PORT_RESTRICTED_CONE_NAT | ADDRESS_AND_PORT_DEPENDENT_MAPPING; - /// Equivalent to: `SYMMETRIC_NAT | IP_POOLING_BEHAVIOR_ARBITRARY | INBOUND_REFRESH_BEHAVIOR_FALSE | NO_PORT_PARITY` + /// Equivalent to: `SYMMETRIC_NAT | IP_POOLING_BEHAVIOR_ARBITRARY | INBOUND_REFRESH_BEHAVIOR_FALSE | NO_PORT_PARITY | NO_WELL_KNOWN_PRESERVATION` /// # Example /// ``` /// use nat_emulation::predefines::HARD_NAT; - /// use nat_emulation::{DestType::*, Nat}; + /// use nat_emulation::{port_ranges::PRIVATE, DestType::*, Nat}; /// let rng = rand::rngs::mock::StepRng::new(0, 1); /// let mut time = 100; /// let timeout = 1000 * 60 * 2; /// - /// let mut nat = Nat::new(HARD_NAT, [11110, 11111, 11112, 11113], 90000..=99999, 49152..=u16::MAX, rng, timeout); + /// let mut nat = Nat::new(HARD_NAT, [11110, 11111, 11112, 11113], 90000..=99999, PRIVATE, rng, usize::MAX, timeout); /// let client_in_addr = nat.assign_internal_address(); - /// let client_in_port = 17; + /// let client_in_port = 25565; /// let server_ex_addr = 22222; /// let server_ex_port0 = 80; /// let server_ex_port1 = 17; @@ -380,21 +384,22 @@ pub mod predefines { /// _ => assert!(false), /// } /// ``` - pub const HARD_NAT: u32 = SYMMETRIC_NAT | IP_POOLING_BEHAVIOR_ARBITRARY | INBOUND_REFRESH_BEHAVIOR_FALSE | NO_PORT_PARITY; + pub const HARD_NAT: u32 = + SYMMETRIC_NAT | IP_POOLING_BEHAVIOR_ARBITRARY | INBOUND_REFRESH_BEHAVIOR_FALSE | NO_PORT_PARITY | NO_WELL_KNOWN_PRESERVATION; /// Equivalent to: `HARD_NAT | INTERNAL_ADDRESS_AND_PORT_HAIRPINNING | OUTBOUND_REFRESH_BEHAVIOR_FALSE | FILTERED_INBOUND_DESTROYS_MAPPING` /// # Example /// ``` /// use nat_emulation::predefines::MISBEHAVING_NAT; - /// use nat_emulation::{DestType, Nat}; + /// use nat_emulation::{port_ranges::PRIVATE, DestType, Nat}; /// let rng = rand::rngs::mock::StepRng::new(0, 1); /// let mut time = 100; /// let timeout = 1000 * 60 * 2; /// /// let nat_ex_addr = 11111; - /// let mut nat = Nat::new(MISBEHAVING_NAT, [nat_ex_addr], 90000..=99999, 49152..=u16::MAX, rng, timeout); + /// let mut nat = Nat::new(MISBEHAVING_NAT, [nat_ex_addr], 90000..=99999, PRIVATE, rng, usize::MAX, timeout); /// /// let client_in_addr = nat.assign_internal_address(); - /// let client_in_port = 17; + /// let client_in_port = 25565; /// let server_ex_addr = 22222; /// let server_ex_port0 = 80; /// let server_ex_port1 = 17; @@ -420,3 +425,18 @@ pub mod predefines { pub const MISBEHAVING_NAT: u32 = HARD_NAT | INTERNAL_ADDRESS_AND_PORT_HAIRPINNING | OUTBOUND_REFRESH_BEHAVIOR_FALSE | FILTERED_INBOUND_DESTROYS_MAPPING; } +/// The standard set of different port ranges used on the internet. +pub mod port_ranges { + use std::ops::RangeInclusive; + + /// The well-known port range, ports 1 to 1023. + pub const WELL_KNOWN: RangeInclusive = 1..=1023; + /// The registered port range, ports 1024 to 49151. + pub const REGISTERED: RangeInclusive = 1024..=49151; + /// The private, or dynamic, port range, ports 49152 to 65535. + pub const PRIVATE: RangeInclusive = 49152..=65535; + /// The combined registered and private port ranges, ports 1024 to 65535. + pub const REGISTERED_AND_PRIVATE: RangeInclusive = 1024..=65535; + /// The valid port numbers from 1 to 65535. + pub const ALL: RangeInclusive = 1..=65535; +}