From af1ccf93a5e4dd38a05ca4213d8184ef2cb39d1a Mon Sep 17 00:00:00 2001
From: Brian O'Connor <bocon@onlab.us>
Date: Mon, 24 Nov 2014 18:00:38 -0800
Subject: [PATCH] Updating NAT class to use gateway interface

Also, passing CLI args to NAT constructor

fixes #437
---
 bin/mn             | 24 +++++++++++++++++++-----
 mininet/nodelib.py | 20 +++++++++++++++-----
 2 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/bin/mn b/bin/mn
index 1933098b..b2bb0060 100755
--- a/bin/mn
+++ b/bin/mn
@@ -32,7 +32,7 @@ from mininet.nodelib import LinuxBridge
 from mininet.link import Link, TCLink
 from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
 from mininet.topolib import TreeTopo, TorusTopo
-from mininet.util import customConstructor
+from mininet.util import customConstructor, splitArgs
 from mininet.util import buildTopo
 
 from functools import partial
@@ -168,6 +168,17 @@ class MininetRunner( object ):
             # Add or modify global variable or class
             globals()[ name ] = value
 
+    def setNat( self, option, opt_str, value, parser ):
+        parser.values.nat = True
+        if parser.rargs and parser.rargs[ 0 ][ 0 ] != '-': #first arg, first char != '-'
+            value = parser.rargs.pop( 0 )
+            _, args, kwargs = splitArgs( opt_str + ',' + value )
+            parser.values.nat_args = args
+            parser.values.nat_kwargs = kwargs
+        else:
+            parser.values.nat_args = []
+            parser.values.nat_kwargs = {}
+
     def parseArgs( self ):
         """Parse command-line args and return options object.
            returns: opts parse options dict"""
@@ -219,9 +230,12 @@ class MininetRunner( object ):
         opts.add_option( '--pin', action='store_true',
                          default=False, help="pin hosts to CPU cores "
                          "(requires --host cfs or --host rt)" )
-        opts.add_option( '--nat', action='store_true',
-                         default=False, help="adds a NAT to the topology "
-                         "that connects Mininet to the physical network" )
+        opts.add_option( '--nat', action='callback', callback=self.setNat,
+                         help="adds a NAT to the topology that connects Mininet hosts"
+                         " to the physical network."
+                         " Warning: This may route any traffic on the machine that uses Mininet's"
+                         " IP subnet into the Mininet network. If you need to change"
+                         " Mininet's IP subnet, see the --ipbase option." )
         opts.add_option( '--version', action='callback', callback=version,
                          help='prints the version and exits' )
         opts.add_option( '--cluster', type='string', default=None,
@@ -319,7 +333,7 @@ class MininetRunner( object ):
                   listenPort=listenPort )
 
         if self.options.nat:
-            nat = mn.addNAT()
+            nat = mn.addNAT( *self.options.nat_args, **self.options.nat_kwargs )
             nat.configDefault()
 
         if self.options.pre:
diff --git a/mininet/nodelib.py b/mininet/nodelib.py
index 54ec1936..f89e20ab 100644
--- a/mininet/nodelib.py
+++ b/mininet/nodelib.py
@@ -5,9 +5,10 @@
 """
 
 from mininet.node import Node, Switch
-from mininet.log import info
+from mininet.log import info, warn
 from mininet.moduledeps import pathCheck
 
+import re
 
 class LinuxBridge( Switch ):
     "Linux Bridge (with optional spanning tree)"
@@ -63,13 +64,13 @@ def setup( cls ):
 class NAT( Node ):
     """NAT: Provides connectivity to external network"""
 
-    def __init__( self, name, inetIntf='eth0', subnet='10.0/8', localIntf=None, **params):
+    def __init__( self, name, inetIntf=None, subnet='10.0/8', localIntf=None, **params):
         super( NAT, self ).__init__( name, **params )
 
         """Start NAT/forwarding between Mininet and external network
         inetIntf: interface for internet access
         subnet: Mininet subnet (default 10.0/8)="""
-        self.inetIntf = inetIntf
+        self.inetIntf = inetIntf if inetIntf else self.getGatewayIntf()
         self.subnet = subnet
         self.localIntf = localIntf
 
@@ -96,7 +97,7 @@ def config( self, **params ):
         self.cmd( 'iptables -I FORWARD -i', self.localIntf, '-d', self.subnet, '-j DROP' )
         self.cmd( 'iptables -A FORWARD -i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
         self.cmd( 'iptables -A FORWARD -i', self.inetIntf, '-d', self.subnet, '-j ACCEPT' )
-        self.cmd( 'iptables -t nat -A POSTROUTING -o ', self.inetIntf, '-j MASQUERADE' )
+        self.cmd( 'iptables -t nat -A POSTROUTING -o ', self.inetIntf, '-s', self.subnet, '-j MASQUERADE' )
 
         # Instruct the kernel to perform forwarding
         self.cmd( 'sysctl net.ipv4.ip_forward=1' )
@@ -108,13 +109,22 @@ def config( self, **params ):
         line = '\niface %s inet manual\n' % intf
         config = open( cfile ).read()
         if ( line ) not in config:
-            info( '*** Adding "' + line.strip() + '" to ' + cfile )
+            info( '*** Adding "' + line.strip() + '" to ' + cfile + '\n' )
             with open( cfile, 'a' ) as f:
                 f.write( line )
         # Probably need to restart network-manager to be safe -
         # hopefully this won't disconnect you
         self.cmd( 'service network-manager restart' )
 
+    def getGatewayIntf( self ):
+        routes = self.cmd( 'ip route show' )
+        match = re.search('default via \S+ dev (\S+)', routes )
+        if match:
+            return match.group( 1 )
+        else:
+            warn( 'There is no default route set. Using eth0 as gateway interface...\n' )
+            return 'eth0'
+
     def terminate( self ):
         """Stop NAT/forwarding between Mininet and external network"""
         # Flush any currently active rules
-- 
GitLab