@@ -398,6 +398,120 @@ impl SysRoute {
398398 pub fn disable_snat_for_local_network ( & self , _local_cidr : & str , _tun_interface : & str , _virtual_ip : & str ) -> crate :: Result < ( ) > {
399399 Ok ( ( ) )
400400 }
401+
402+ /// Enable DNAT/NETMAP for CIDR mapping (Linux only)
403+ /// Maps destination IPs from mapped CIDR to real CIDR
404+ /// Uses NETMAP target: iptables -t nat -A PREROUTING -d <mapped_cidr> -j NETMAP --to <real_cidr>
405+ ///
406+ /// # Arguments
407+ /// * `mapped_cidr` - The CIDR that other clients see (e.g., "192.168.11.0/24")
408+ /// * `real_cidr` - The real CIDR network (e.g., "192.168.10.0/24")
409+ ///
410+ /// # Example
411+ /// When a packet arrives with destination IP in `mapped_cidr`, it will be translated
412+ /// to the corresponding IP in `real_cidr` before being forwarded to the local network.
413+ #[ cfg( target_os = "linux" ) ]
414+ pub fn enable_cidr_dnat ( & self , mapped_cidr : & str , real_cidr : & str ) -> crate :: Result < ( ) > {
415+ // Check if NETMAP rule already exists: iptables -t nat -C PREROUTING -d <mapped_cidr> -j NETMAP --to <real_cidr>
416+ let check_output = Command :: new ( "iptables" )
417+ . args ( [
418+ "-t" , "nat" ,
419+ "-C" , "PREROUTING" ,
420+ "-d" , mapped_cidr,
421+ "-j" , "NETMAP" ,
422+ "--to" , real_cidr,
423+ ] )
424+ . output ( ) ;
425+
426+ match check_output {
427+ Ok ( output) if output. status . success ( ) => {
428+ tracing:: debug!( "DNAT rule already exists: {} -> {}" , mapped_cidr, real_cidr) ;
429+ return Ok ( ( ) ) ;
430+ }
431+ _ => { }
432+ }
433+
434+ // Add NETMAP rule: iptables -t nat -A PREROUTING -d <mapped_cidr> -j NETMAP --to <real_cidr>
435+ let output = Command :: new ( "iptables" )
436+ . args ( [
437+ "-t" , "nat" ,
438+ "-A" , "PREROUTING" ,
439+ "-d" , mapped_cidr,
440+ "-j" , "NETMAP" ,
441+ "--to" , real_cidr,
442+ ] )
443+ . output ( )
444+ . map_err ( |e| {
445+ if e. kind ( ) == std:: io:: ErrorKind :: NotFound {
446+ format ! (
447+ "iptables command not found. CIDR mapping requires iptables with NETMAP support.\n \
448+ Please install iptables and ensure your kernel supports NETMAP target.\n \
449+ NETMAP requires Linux kernel 2.6.32+ with netfilter NETMAP module."
450+ )
451+ } else {
452+ format ! ( "Failed to execute iptables command: {}" , e)
453+ }
454+ } ) ?;
455+
456+ if !output. status . success ( ) {
457+ let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
458+ // Check if NETMAP is not supported
459+ if stderr. contains ( "No chain/target/match" ) || stderr. contains ( "NETMAP" ) {
460+ return Err ( format ! (
461+ "NETMAP target not supported. CIDR mapping requires kernel support for NETMAP.\n \
462+ Please ensure your kernel has NETMAP support (Linux 2.6.32+) or use a different approach.\n \
463+ Error: {}", stderr
464+ ) . into ( ) ) ;
465+ }
466+ return Err ( format ! ( "Failed to add DNAT rule: {}" , stderr) . into ( ) ) ;
467+ }
468+
469+ tracing:: info!( "Added DNAT rule: {} -> {}" , mapped_cidr, real_cidr) ;
470+ Ok ( ( ) )
471+ }
472+
473+ /// Disable DNAT/NETMAP for CIDR mapping (Linux only)
474+ /// Removes the NETMAP rule that was previously added
475+ ///
476+ /// # Arguments
477+ /// * `mapped_cidr` - The mapped CIDR (e.g., "192.168.11.0/24")
478+ /// * `real_cidr` - The real CIDR (e.g., "192.168.10.0/24")
479+ #[ cfg( target_os = "linux" ) ]
480+ pub fn disable_cidr_dnat ( & self , mapped_cidr : & str , real_cidr : & str ) -> crate :: Result < ( ) > {
481+ let output = Command :: new ( "iptables" )
482+ . args ( [
483+ "-t" , "nat" ,
484+ "-D" , "PREROUTING" ,
485+ "-d" , mapped_cidr,
486+ "-j" , "NETMAP" ,
487+ "--to" , real_cidr,
488+ ] )
489+ . output ( )
490+ . map_err ( |e| format ! ( "Failed to execute iptables command: {}" , e) ) ?;
491+
492+ if !output. status . success ( ) {
493+ let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
494+ // Ignore "not found" error (rule already deleted)
495+ if stderr. contains ( "not found" ) || stderr. contains ( "找不到" ) || stderr. contains ( "No rule" ) {
496+ tracing:: debug!( "DNAT rule not found (already deleted): {} -> {}" , mapped_cidr, real_cidr) ;
497+ return Ok ( ( ) ) ;
498+ }
499+ return Err ( format ! ( "Failed to delete DNAT rule: {}" , stderr) . into ( ) ) ;
500+ }
501+
502+ tracing:: info!( "Deleted DNAT rule: {} -> {}" , mapped_cidr, real_cidr) ;
503+ Ok ( ( ) )
504+ }
505+
506+ #[ cfg( not( target_os = "linux" ) ) ]
507+ pub fn enable_cidr_dnat ( & self , _mapped_cidr : & str , _real_cidr : & str ) -> crate :: Result < ( ) > {
508+ Err ( "CIDR mapping DNAT is only supported on Linux" . into ( ) )
509+ }
510+
511+ #[ cfg( not( target_os = "linux" ) ) ]
512+ pub fn disable_cidr_dnat ( & self , _mapped_cidr : & str , _real_cidr : & str ) -> crate :: Result < ( ) > {
513+ Err ( "CIDR mapping DNAT is only supported on Linux" . into ( ) )
514+ }
401515}
402516
403517impl Default for SysRoute {
0 commit comments