xref: /webrtc/util/src/vnet/nat.rs (revision 97921129)
1 #[cfg(test)]
2 mod nat_test;
3 
4 use crate::error::*;
5 use crate::vnet::chunk::Chunk;
6 use crate::vnet::net::UDP_STR;
7 
8 use std::collections::{HashMap, HashSet};
9 use std::net::IpAddr;
10 use std::ops::Add;
11 use std::sync::atomic::{AtomicU16, Ordering};
12 use std::sync::Arc;
13 use std::time::SystemTime;
14 use tokio::sync::Mutex;
15 use tokio::time::Duration;
16 
17 const DEFAULT_NAT_MAPPING_LIFE_TIME: Duration = Duration::from_secs(30);
18 
19 // EndpointDependencyType defines a type of behavioral dependendency on the
20 // remote endpoint's IP address or port number. This is used for the two
21 // kinds of behaviors:
22 //  - Port Mapping behavior
23 //  - Filtering behavior
24 // See: https://tools.ietf.org/html/rfc4787
25 #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
26 pub enum EndpointDependencyType {
27     // EndpointIndependent means the behavior is independent of the endpoint's address or port
28     #[default]
29     EndpointIndependent,
30     // EndpointAddrDependent means the behavior is dependent on the endpoint's address
31     EndpointAddrDependent,
32     // EndpointAddrPortDependent means the behavior is dependent on the endpoint's address and port
33     EndpointAddrPortDependent,
34 }
35 
36 // NATMode defines basic behavior of the NAT
37 #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
38 pub enum NatMode {
39     // NATModeNormal means the NAT behaves as a standard NAPT (RFC 2663).
40     #[default]
41     Normal,
42     // NATModeNAT1To1 exhibits 1:1 DNAT where the external IP address is statically mapped to
43     // a specific local IP address with port number is preserved always between them.
44     // When this mode is selected, mapping_behavior, filtering_behavior, port_preservation and
45     // mapping_life_time of NATType are ignored.
46     Nat1To1,
47 }
48 
49 // NATType has a set of parameters that define the behavior of NAT.
50 #[derive(Default, Debug, Copy, Clone)]
51 pub struct NatType {
52     pub mode: NatMode,
53     pub mapping_behavior: EndpointDependencyType,
54     pub filtering_behavior: EndpointDependencyType,
55     pub hair_pining: bool,       // Not implemented yet
56     pub port_preservation: bool, // Not implemented yet
57     pub mapping_life_time: Duration,
58 }
59 
60 #[derive(Default, Debug, Clone)]
61 pub(crate) struct NatConfig {
62     pub(crate) name: String,
63     pub(crate) nat_type: NatType,
64     pub(crate) mapped_ips: Vec<IpAddr>, // mapped IPv4
65     pub(crate) local_ips: Vec<IpAddr>,  // local IPv4, required only when the mode is NATModeNAT1To1
66 }
67 
68 #[derive(Debug, Clone)]
69 pub(crate) struct Mapping {
70     proto: String,                        // "udp" or "tcp"
71     local: String,                        // "<local-ip>:<local-port>"
72     mapped: String,                       // "<mapped-ip>:<mapped-port>"
73     bound: String,                        // key: "[<remote-ip>[:<remote-port>]]"
74     filters: Arc<Mutex<HashSet<String>>>, // key: "[<remote-ip>[:<remote-port>]]"
75     expires: Arc<Mutex<SystemTime>>,      // time to expire
76 }
77 
78 impl Default for Mapping {
default() -> Self79     fn default() -> Self {
80         Mapping {
81             proto: String::new(),                             // "udp" or "tcp"
82             local: String::new(),                             // "<local-ip>:<local-port>"
83             mapped: String::new(),                            // "<mapped-ip>:<mapped-port>"
84             bound: String::new(), // key: "[<remote-ip>[:<remote-port>]]"
85             filters: Arc::new(Mutex::new(HashSet::new())), // key: "[<remote-ip>[:<remote-port>]]"
86             expires: Arc::new(Mutex::new(SystemTime::now())), // time to expire
87         }
88     }
89 }
90 
91 #[derive(Default, Debug, Clone)]
92 pub(crate) struct NetworkAddressTranslator {
93     pub(crate) name: String,
94     pub(crate) nat_type: NatType,
95     pub(crate) mapped_ips: Vec<IpAddr>, // mapped IPv4
96     pub(crate) local_ips: Vec<IpAddr>,  // local IPv4, required only when the mode is NATModeNAT1To1
97     pub(crate) outbound_map: Arc<Mutex<HashMap<String, Arc<Mapping>>>>, // key: "<proto>:<local-ip>:<local-port>[:remote-ip[:remote-port]]
98     pub(crate) inbound_map: Arc<Mutex<HashMap<String, Arc<Mapping>>>>, // key: "<proto>:<mapped-ip>:<mapped-port>"
99     pub(crate) udp_port_counter: Arc<AtomicU16>,
100 }
101 
102 impl NetworkAddressTranslator {
new(config: NatConfig) -> Result<Self>103     pub(crate) fn new(config: NatConfig) -> Result<Self> {
104         let mut nat_type = config.nat_type;
105 
106         if nat_type.mode == NatMode::Nat1To1 {
107             // 1:1 NAT behavior
108             nat_type.mapping_behavior = EndpointDependencyType::EndpointIndependent;
109             nat_type.filtering_behavior = EndpointDependencyType::EndpointIndependent;
110             nat_type.port_preservation = true;
111             nat_type.mapping_life_time = Duration::from_secs(0);
112 
113             if config.mapped_ips.is_empty() {
114                 return Err(Error::ErrNatRequriesMapping);
115             }
116             if config.mapped_ips.len() != config.local_ips.len() {
117                 return Err(Error::ErrMismatchLengthIp);
118             }
119         } else {
120             // Normal (NAPT) behavior
121             nat_type.mode = NatMode::Normal;
122             if nat_type.mapping_life_time == Duration::from_secs(0) {
123                 nat_type.mapping_life_time = DEFAULT_NAT_MAPPING_LIFE_TIME;
124             }
125         }
126 
127         Ok(NetworkAddressTranslator {
128             name: config.name,
129             nat_type,
130             mapped_ips: config.mapped_ips,
131             local_ips: config.local_ips,
132             outbound_map: Arc::new(Mutex::new(HashMap::new())),
133             inbound_map: Arc::new(Mutex::new(HashMap::new())),
134             udp_port_counter: Arc::new(AtomicU16::new(0)),
135         })
136     }
137 
get_paired_mapped_ip(&self, loc_ip: &IpAddr) -> Option<&IpAddr>138     pub(crate) fn get_paired_mapped_ip(&self, loc_ip: &IpAddr) -> Option<&IpAddr> {
139         for (i, ip) in self.local_ips.iter().enumerate() {
140             if ip == loc_ip {
141                 return self.mapped_ips.get(i);
142             }
143         }
144         None
145     }
146 
get_paired_local_ip(&self, mapped_ip: &IpAddr) -> Option<&IpAddr>147     pub(crate) fn get_paired_local_ip(&self, mapped_ip: &IpAddr) -> Option<&IpAddr> {
148         for (i, ip) in self.mapped_ips.iter().enumerate() {
149             if ip == mapped_ip {
150                 return self.local_ips.get(i);
151             }
152         }
153         None
154     }
155 
translate_outbound( &self, from: &(dyn Chunk + Send + Sync), ) -> Result<Option<Box<dyn Chunk + Send + Sync>>>156     pub(crate) async fn translate_outbound(
157         &self,
158         from: &(dyn Chunk + Send + Sync),
159     ) -> Result<Option<Box<dyn Chunk + Send + Sync>>> {
160         let mut to = from.clone_to();
161 
162         if from.network() == UDP_STR {
163             if self.nat_type.mode == NatMode::Nat1To1 {
164                 // 1:1 NAT behavior
165                 let src_addr = from.source_addr();
166                 if let Some(src_ip) = self.get_paired_mapped_ip(&src_addr.ip()) {
167                     to.set_source_addr(&format!("{}:{}", src_ip, src_addr.port()))?;
168                 } else {
169                     log::debug!(
170                         "[{}] drop outbound chunk {} with not route",
171                         self.name,
172                         from
173                     );
174                     return Ok(None); // silently discard
175                 }
176             } else {
177                 // Normal (NAPT) behavior
178                 let bound = match self.nat_type.mapping_behavior {
179                     EndpointDependencyType::EndpointIndependent => "".to_owned(),
180                     EndpointDependencyType::EndpointAddrDependent => {
181                         from.get_destination_ip().to_string()
182                     }
183                     EndpointDependencyType::EndpointAddrPortDependent => {
184                         from.destination_addr().to_string()
185                     }
186                 };
187 
188                 let filter_key = match self.nat_type.filtering_behavior {
189                     EndpointDependencyType::EndpointIndependent => "".to_owned(),
190                     EndpointDependencyType::EndpointAddrDependent => {
191                         from.get_destination_ip().to_string()
192                     }
193                     EndpointDependencyType::EndpointAddrPortDependent => {
194                         from.destination_addr().to_string()
195                     }
196                 };
197 
198                 let o_key = format!("udp:{}:{}", from.source_addr(), bound);
199                 let name = self.name.clone();
200 
201                 let m_mapped = if let Some(m) = self.find_outbound_mapping(&o_key).await {
202                     let mut filters = m.filters.lock().await;
203                     if !filters.contains(&filter_key) {
204                         log::debug!(
205                             "[{}] permit access from {} to {}",
206                             name,
207                             filter_key,
208                             m.mapped
209                         );
210                         filters.insert(filter_key);
211                     }
212                     m.mapped.clone()
213                 } else {
214                     // Create a new Mapping
215                     let udp_port_counter = self.udp_port_counter.load(Ordering::SeqCst);
216                     let mapped_port = 0xC000 + udp_port_counter;
217                     if udp_port_counter == 0xFFFF - 0xC000 {
218                         self.udp_port_counter.store(0, Ordering::SeqCst);
219                     } else {
220                         self.udp_port_counter.fetch_add(1, Ordering::SeqCst);
221                     }
222 
223                     let m = if let Some(mapped_ips_first) = self.mapped_ips.first() {
224                         Mapping {
225                             proto: "udp".to_owned(),
226                             local: from.source_addr().to_string(),
227                             bound,
228                             mapped: format!("{mapped_ips_first}:{mapped_port}"),
229                             filters: Arc::new(Mutex::new(HashSet::new())),
230                             expires: Arc::new(Mutex::new(
231                                 SystemTime::now().add(self.nat_type.mapping_life_time),
232                             )),
233                         }
234                     } else {
235                         return Err(Error::ErrNatRequriesMapping);
236                     };
237 
238                     {
239                         let mut outbound_map = self.outbound_map.lock().await;
240                         outbound_map.insert(o_key.clone(), Arc::new(m.clone()));
241                     }
242 
243                     let i_key = format!("udp:{}", m.mapped);
244 
245                     log::debug!(
246                         "[{}] created a new NAT binding oKey={} i_key={}",
247                         self.name,
248                         o_key,
249                         i_key
250                     );
251                     log::debug!(
252                         "[{}] permit access from {} to {}",
253                         self.name,
254                         filter_key,
255                         m.mapped
256                     );
257 
258                     {
259                         let mut filters = m.filters.lock().await;
260                         filters.insert(filter_key);
261                     }
262 
263                     let m_mapped = m.mapped.clone();
264                     {
265                         let mut inbound_map = self.inbound_map.lock().await;
266                         inbound_map.insert(i_key, Arc::new(m));
267                     }
268                     m_mapped
269                 };
270 
271                 to.set_source_addr(&m_mapped)?;
272             }
273 
274             log::debug!(
275                 "[{}] translate outbound chunk from {} to {}",
276                 self.name,
277                 from,
278                 to
279             );
280 
281             return Ok(Some(to));
282         }
283 
284         Err(Error::ErrNonUdpTranslationNotSupported)
285     }
286 
translate_inbound( &self, from: &(dyn Chunk + Send + Sync), ) -> Result<Option<Box<dyn Chunk + Send + Sync>>>287     pub(crate) async fn translate_inbound(
288         &self,
289         from: &(dyn Chunk + Send + Sync),
290     ) -> Result<Option<Box<dyn Chunk + Send + Sync>>> {
291         let mut to = from.clone_to();
292 
293         if from.network() == UDP_STR {
294             if self.nat_type.mode == NatMode::Nat1To1 {
295                 // 1:1 NAT behavior
296                 let dst_addr = from.destination_addr();
297                 if let Some(dst_ip) = self.get_paired_local_ip(&dst_addr.ip()) {
298                     let dst_port = from.destination_addr().port();
299                     to.set_destination_addr(&format!("{dst_ip}:{dst_port}"))?;
300                 } else {
301                     return Err(Error::Other(format!(
302                         "drop {} as {:?}",
303                         from,
304                         Error::ErrNoAssociatedLocalAddress
305                     )));
306                 }
307             } else {
308                 // Normal (NAPT) behavior
309                 let filter_key = match self.nat_type.filtering_behavior {
310                     EndpointDependencyType::EndpointIndependent => "".to_owned(),
311                     EndpointDependencyType::EndpointAddrDependent => {
312                         from.get_source_ip().to_string()
313                     }
314                     EndpointDependencyType::EndpointAddrPortDependent => {
315                         from.source_addr().to_string()
316                     }
317                 };
318 
319                 let i_key = format!("udp:{}", from.destination_addr());
320                 if let Some(m) = self.find_inbound_mapping(&i_key).await {
321                     {
322                         let filters = m.filters.lock().await;
323                         if !filters.contains(&filter_key) {
324                             return Err(Error::Other(format!(
325                                 "drop {} as the remote {} {:?}",
326                                 from,
327                                 filter_key,
328                                 Error::ErrHasNoPermission
329                             )));
330                         }
331                     }
332 
333                     // See RFC 4847 Section 4.3.  Mapping Refresh
334                     // a) Inbound refresh may be useful for applications with no outgoing
335                     //   UDP traffic.  However, allowing inbound refresh may allow an
336                     //   external attacker or misbehaving application to keep a Mapping
337                     //   alive indefinitely.  This may be a security risk.  Also, if the
338                     //   process is repeated with different ports, over time, it could
339                     //   use up all the ports on the NAT.
340 
341                     to.set_destination_addr(&m.local)?;
342                 } else {
343                     return Err(Error::Other(format!(
344                         "drop {} as {:?}",
345                         from,
346                         Error::ErrNoNatBindingFound
347                     )));
348                 }
349             }
350 
351             log::debug!(
352                 "[{}] translate inbound chunk from {} to {}",
353                 self.name,
354                 from,
355                 to
356             );
357 
358             return Ok(Some(to));
359         }
360 
361         Err(Error::ErrNonUdpTranslationNotSupported)
362     }
363 
364     // caller must hold the mutex
find_outbound_mapping(&self, o_key: &str) -> Option<Arc<Mapping>>365     pub(crate) async fn find_outbound_mapping(&self, o_key: &str) -> Option<Arc<Mapping>> {
366         let mapping_life_time = self.nat_type.mapping_life_time;
367         let mut expired = false;
368         let (in_key, out_key) = {
369             let outbound_map = self.outbound_map.lock().await;
370             if let Some(m) = outbound_map.get(o_key) {
371                 let now = SystemTime::now();
372 
373                 {
374                     let mut expires = m.expires.lock().await;
375                     // check if this Mapping is expired
376                     if now.duration_since(*expires).is_ok() {
377                         expired = true;
378                     } else {
379                         *expires = now.add(mapping_life_time);
380                     }
381                 }
382                 (
383                     NetworkAddressTranslator::get_inbound_map_key(m),
384                     NetworkAddressTranslator::get_outbound_map_key(m),
385                 )
386             } else {
387                 (String::new(), String::new())
388             }
389         };
390 
391         if expired {
392             {
393                 let mut inbound_map = self.inbound_map.lock().await;
394                 inbound_map.remove(&in_key);
395             }
396             {
397                 let mut outbound_map = self.outbound_map.lock().await;
398                 outbound_map.remove(&out_key);
399             }
400         }
401 
402         let outbound_map = self.outbound_map.lock().await;
403         outbound_map.get(o_key).map(Arc::clone)
404     }
405 
406     // caller must hold the mutex
find_inbound_mapping(&self, i_key: &str) -> Option<Arc<Mapping>>407     pub(crate) async fn find_inbound_mapping(&self, i_key: &str) -> Option<Arc<Mapping>> {
408         let mut expired = false;
409         let (in_key, out_key) = {
410             let inbound_map = self.inbound_map.lock().await;
411             if let Some(m) = inbound_map.get(i_key) {
412                 let now = SystemTime::now();
413 
414                 {
415                     let expires = m.expires.lock().await;
416                     // check if this Mapping is expired
417                     if now.duration_since(*expires).is_ok() {
418                         expired = true;
419                     }
420                 }
421                 (
422                     NetworkAddressTranslator::get_inbound_map_key(m),
423                     NetworkAddressTranslator::get_outbound_map_key(m),
424                 )
425             } else {
426                 (String::new(), String::new())
427             }
428         };
429 
430         if expired {
431             {
432                 let mut inbound_map = self.inbound_map.lock().await;
433                 inbound_map.remove(&in_key);
434             }
435             {
436                 let mut outbound_map = self.outbound_map.lock().await;
437                 outbound_map.remove(&out_key);
438             }
439         }
440 
441         let inbound_map = self.inbound_map.lock().await;
442         inbound_map.get(i_key).map(Arc::clone)
443     }
444 
445     // caller must hold the mutex
get_outbound_map_key(m: &Mapping) -> String446     fn get_outbound_map_key(m: &Mapping) -> String {
447         format!("{}:{}:{}", m.proto, m.local, m.bound)
448     }
449 
get_inbound_map_key(m: &Mapping) -> String450     fn get_inbound_map_key(m: &Mapping) -> String {
451         format!("{}:{}", m.proto, m.mapped)
452     }
453 
inbound_map_len(&self) -> usize454     async fn inbound_map_len(&self) -> usize {
455         let inbound_map = self.inbound_map.lock().await;
456         inbound_map.len()
457     }
458 
outbound_map_len(&self) -> usize459     async fn outbound_map_len(&self) -> usize {
460         let outbound_map = self.outbound_map.lock().await;
461         outbound_map.len()
462     }
463 }
464