From 47b9466fad7d2751be0487f720679c0bfbe9cf0e Mon Sep 17 00:00:00 2001 From: Brian O'Connor Date: Tue, 16 Jul 2013 17:10:12 -0700 Subject: [PATCH 01/10] Adding NAT class Includes automatic NAT feature (mn --nat) and addNAT convenience method for topologies fixes #111 --- bin/mn | 5 ++++ mininet/net.py | 43 +++++++++++++++++++++++++++++---- mininet/node.py | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ mininet/topo.py | 10 ++++++++ 4 files changed, 118 insertions(+), 4 deletions(-) diff --git a/bin/mn b/bin/mn index 71c76ec..0424d35 100755 --- a/bin/mn +++ b/bin/mn @@ -193,6 +193,9 @@ 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( '--version', action='callback', callback=version ) self.options, self.args = opts.parse_args() @@ -223,6 +226,8 @@ class MininetRunner( object ): start = time.time() topo = buildTopo( TOPOS, self.options.topo ) + if self.options.nat: + topo.addNAT() switch = customConstructor( SWITCHES, self.options.switch ) host = customConstructor( HOSTS, self.options.host ) controller = customConstructor( CONTROLLERS, self.options.controller ) diff --git a/mininet/net.py b/mininet/net.py index 4994f9e..87f7b47 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -95,7 +95,7 @@ from itertools import chain from mininet.cli import CLI from mininet.log import info, error, debug, output -from mininet.node import Host, OVSKernelSwitch, Controller +from mininet.node import Host, OVSKernelSwitch, Controller, NAT from mininet.link import Link, Intf from mininet.util import quietRun, fixLimits, numCores, ensureRoot from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd @@ -112,7 +112,8 @@ class Mininet( object ): build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8', inNamespace=False, autoSetMacs=False, autoStaticArp=False, autoPinCpus=False, - listenPort=None ): + listenPort=None, + gateway=None ): """Create Mininet object. topo: Topo (topology) object or None switch: default Switch class @@ -129,7 +130,8 @@ class Mininet( object ): autoStaticArp: set all-pairs static MAC addrs? autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)? listenPort: base listening port to open; will be incremented for - each additional switch in the net if inNamespace=False""" + each additional switch in the net if inNamespace=False + gateway: node that provides connectivity to the Internet""" self.topo = topo self.switch = switch self.host = host @@ -148,6 +150,7 @@ class Mininet( object ): self.numCores = numCores() self.nextCore = 0 # next core for pinning hosts to CPUs self.listenPort = listenPort + self.gateway = gateway self.hosts = [] self.switches = [] @@ -163,6 +166,9 @@ class Mininet( object ): if topo and build: self.build() + # list of Node subclasses that provide gateway service + gateways = [ NAT ] + def addHost( self, name, cls=None, **params ): """Add host. name: name of host to add @@ -175,17 +181,22 @@ class Mininet( object ): prefixLen=self.prefixLen ) + '/%s' % self.prefixLen } if self.autoSetMacs: - defaults[ 'mac'] = macColonHex( self.nextIP ) + defaults[ 'mac' ] = macColonHex( self.nextIP ) if self.autoPinCpus: defaults[ 'cores' ] = self.nextCore self.nextCore = ( self.nextCore + 1 ) % self.numCores self.nextIP += 1 defaults.update( params ) + # TODO: clean this up + if params.get( 'isNAT', False ): + cls = NAT if not cls: cls = self.host h = cls( name, **defaults ) self.hosts.append( h ) self.nameToNode[ name ] = h + if cls in self.gateways: + self.gateway = h return h def addSwitch( self, name, cls=None, **params ): @@ -227,6 +238,16 @@ class Mininet( object ): self.nameToNode[ name ] = controller_new return controller_new + # TODO: incomplete + def addNAT( self, name='nat0', connect=True, **params ): + nat = self.addHost( name, cls=NAT, **params ) + # find first switch and create link + print "net/addNAT" + if connect: + #connect the nat to the first switch + self.addLink( nat, self.switches[ 0 ] ) + return nat + # BL: We now have four ways to look up nodes # This may (should?) be cleaned up in the future. def getNodeByName( self, *args ): @@ -305,6 +326,19 @@ class Mininet( object ): host.cmd( 'ifconfig lo up' ) info( '\n' ) + def configGateway( self ): + """Add gateway routes to all hosts if the networks has a gateway.""" + if self.gateway: + gatewayIP = self.gateway.defaultIntf().IP() + for host in self.hosts: + if host.inNamespace and self.gateway: + host.cmd( 'ip route flush root 0/0' ) + host.cmd( 'route add -net', self.ipBase, 'dev', host.defaultIntf() ) + host.cmd( 'route add default gw', gatewayIP ) + else: + # Don't mess with hosts in the root namespace + pass + def buildFromTopo( self, topo=None ): """Build mininet from a topology object At the end of this function, everything should be connected @@ -363,6 +397,7 @@ class Mininet( object ): self.startTerms() if self.autoStaticArp: self.staticArp() + self.configGateway() self.built = True def startTerms( self ): diff --git a/mininet/node.py b/mininet/node.py index 9e93957..17e153c 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -1243,3 +1243,67 @@ class RemoteController( Controller ): warn( "Unable to contact the remote controller" " at %s:%d\n" % ( self.ip, self.port ) ) +class NAT( Node ): + """NAT: Provides connectivity to external network""" + + def __init__( self, name, inetIntf='eth0', subnet='10.0/8', **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.subnet = subnet #TODO: get subnet from Mininet directly + + def config( self, inetIntf='eth0', subnet='10.0/8', **params ): + super( NAT, self).config( **params ) + """Configure the NAT and iptables""" + + # Identify the interface connecting to the mininet network + localIntf = self.defaultIntf() + self.cmd( 'sysctl net.ipv4.ip_forward=0' ) + + # Flush any currently active rules + # TODO: is this safe? + self.cmd( 'iptables -F' ) + self.cmd( 'iptables -t nat -F' ) + + # Create default entries for unmatched traffic + self.cmd( 'iptables -P INPUT ACCEPT' ) + self.cmd( 'iptables -P OUTPUT ACCEPT' ) + self.cmd( 'iptables -P FORWARD DROP' ) + + # Configure NAT + self.cmd( 'iptables -I FORWARD -i', localIntf, '-d', self.subnet, '-j DROP' ) + self.cmd( 'iptables -A FORWARD -i', 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' ) + + # Instruct the kernel to perform forwarding + self.cmd( 'sysctl net.ipv4.ip_forward=1' ) + + # Prevent network-manager from messing with our interface + # by specifying manual configuration in /etc/network/interfaces + intf = localIntf + cfile = '/etc/network/interfaces' + line = '\niface %s inet manual\n' % intf + config = open( cfile ).read() + if ( line ) not in config: + info( '*** Adding "' + line.strip() + '" to ' + cfile ) + 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 terminate( self ): + """Stop NAT/forwarding between Mininet and external network""" + # Flush any currently active rules + # TODO: is this safe? + self.cmd( 'iptables -F' ) + self.cmd( 'iptables -t nat -F' ) + + # Instruct the kernel to stop forwarding + self.cmd( 'sysctl net.ipv4.ip_forward=0' ) + + super( NAT, self ).terminate() diff --git a/mininet/topo.py b/mininet/topo.py index 2ab455c..9de6eff 100644 --- a/mininet/topo.py +++ b/mininet/topo.py @@ -89,6 +89,16 @@ class Topo(object): result = self.addNode(name, isSwitch=True, **opts) return result + def addNAT(self, name='nat', connect=True, **opts): + """Convenience method: Add NAT to graph. + name: NAT name + connect: True will automatically connect to the first switch""" + nat = self.addNode(name, isNAT=True, inNamespace=False) + if connect: + # connect the NAT to the first switch + self.addLink(name, self.switches()[ 0 ]) + return nat + def addLink(self, node1, node2, port1=None, port2=None, **opts): """node1, node2: nodes to link together From bceb298edbd6c09972628faf3c54ae800b22ae5d Mon Sep 17 00:00:00 2001 From: Brian O'Connor Date: Tue, 16 Jul 2013 17:10:12 -0700 Subject: [PATCH 02/10] Adding NAT class Includes automatic NAT feature (mn --nat) and addNAT convenience method for topologies fixes #111 --- mininet/node.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mininet/node.py b/mininet/node.py index 17e153c..f572993 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -1307,3 +1307,4 @@ class NAT( Node ): self.cmd( 'sysctl net.ipv4.ip_forward=0' ) super( NAT, self ).terminate() + From 3f2355a36aacdf7ac565fadb276dfef76aee1746 Mon Sep 17 00:00:00 2001 From: Brian O'Connor Date: Thu, 3 Oct 2013 15:15:20 -0700 Subject: [PATCH 03/10] undoing gateway in net and removing addNAT helpers --- mininet/net.py | 20 ++++++++------------ mininet/node.py | 21 +++++++++++++++------ mininet/topo.py | 6 ++++-- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index 87f7b47..64174fc 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -112,8 +112,7 @@ class Mininet( object ): build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8', inNamespace=False, autoSetMacs=False, autoStaticArp=False, autoPinCpus=False, - listenPort=None, - gateway=None ): + listenPort=None ): """Create Mininet object. topo: Topo (topology) object or None switch: default Switch class @@ -130,8 +129,7 @@ class Mininet( object ): autoStaticArp: set all-pairs static MAC addrs? autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)? listenPort: base listening port to open; will be incremented for - each additional switch in the net if inNamespace=False - gateway: node that provides connectivity to the Internet""" + each additional switch in the net if inNamespace=False""" self.topo = topo self.switch = switch self.host = host @@ -150,7 +148,6 @@ class Mininet( object ): self.numCores = numCores() self.nextCore = 0 # next core for pinning hosts to CPUs self.listenPort = listenPort - self.gateway = gateway self.hosts = [] self.switches = [] @@ -166,9 +163,6 @@ class Mininet( object ): if topo and build: self.build() - # list of Node subclasses that provide gateway service - gateways = [ NAT ] - def addHost( self, name, cls=None, **params ): """Add host. name: name of host to add @@ -189,14 +183,13 @@ class Mininet( object ): defaults.update( params ) # TODO: clean this up if params.get( 'isNAT', False ): + print "***** &&&&&& !!!! nat nat nat" cls = NAT if not cls: cls = self.host h = cls( name, **defaults ) self.hosts.append( h ) self.nameToNode[ name ] = h - if cls in self.gateways: - self.gateway = h return h def addSwitch( self, name, cls=None, **params ): @@ -242,7 +235,7 @@ class Mininet( object ): def addNAT( self, name='nat0', connect=True, **params ): nat = self.addHost( name, cls=NAT, **params ) # find first switch and create link - print "net/addNAT" + print "******* &&&&&& net/addNAT" if connect: #connect the nat to the first switch self.addLink( nat, self.switches[ 0 ] ) @@ -326,6 +319,7 @@ class Mininet( object ): host.cmd( 'ifconfig lo up' ) info( '\n' ) + ''' TODO: remove this! def configGateway( self ): """Add gateway routes to all hosts if the networks has a gateway.""" if self.gateway: @@ -338,6 +332,7 @@ class Mininet( object ): else: # Don't mess with hosts in the root namespace pass + ''' def buildFromTopo( self, topo=None ): """Build mininet from a topology object @@ -397,7 +392,8 @@ class Mininet( object ): self.startTerms() if self.autoStaticArp: self.staticArp() - self.configGateway() + # TODO: remove this + #self.configGateway() self.built = True def startTerms( self ): diff --git a/mininet/node.py b/mininet/node.py index f572993..d6e8f87 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -1246,7 +1246,7 @@ class RemoteController( Controller ): class NAT( Node ): """NAT: Provides connectivity to external network""" - def __init__( self, name, inetIntf='eth0', subnet='10.0/8', **params): + def __init__( self, name, inetIntf='eth0', subnet='10.0/8', localIntf=None, **params): super( NAT, self ).__init__( name, **params ) """Start NAT/forwarding between Mininet and external network @@ -1254,13 +1254,22 @@ class NAT( Node ): subnet: Mininet subnet (default 10.0/8)=""" self.inetIntf = inetIntf self.subnet = subnet #TODO: get subnet from Mininet directly + self.localIntf = localIntf - def config( self, inetIntf='eth0', subnet='10.0/8', **params ): + def config( self, **params ): super( NAT, self).config( **params ) """Configure the NAT and iptables""" + if not self.localIntf: + self.localIntf = self.defaultIntf() + + #------------------------- + print "inetIntf:", self.inetIntf + print "subnet:", self.subnet # Identify the interface connecting to the mininet network - localIntf = self.defaultIntf() + print "LocalIntf:", self.localIntf + #------------------------- + self.cmd( 'sysctl net.ipv4.ip_forward=0' ) # Flush any currently active rules @@ -1274,8 +1283,8 @@ class NAT( Node ): self.cmd( 'iptables -P FORWARD DROP' ) # Configure NAT - self.cmd( 'iptables -I FORWARD -i', localIntf, '-d', self.subnet, '-j DROP' ) - self.cmd( 'iptables -A FORWARD -i', localIntf, '-s', self.subnet, '-j ACCEPT' ) + 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' ) @@ -1284,7 +1293,7 @@ class NAT( Node ): # Prevent network-manager from messing with our interface # by specifying manual configuration in /etc/network/interfaces - intf = localIntf + intf = self.localIntf cfile = '/etc/network/interfaces' line = '\niface %s inet manual\n' % intf config = open( cfile ).read() diff --git a/mininet/topo.py b/mininet/topo.py index 9de6eff..4ae447c 100644 --- a/mininet/topo.py +++ b/mininet/topo.py @@ -12,6 +12,7 @@ setup for testing, and can even be emulated with the Mininet package. ''' from mininet.util import irange, natural, naturalSeq +from mininet.node import NAT class MultiGraph( object ): "Utility class to track nodes and edges - replaces networkx.Graph" @@ -89,11 +90,12 @@ class Topo(object): result = self.addNode(name, isSwitch=True, **opts) return result - def addNAT(self, name='nat', connect=True, **opts): + def addNAT(self, name='nat', connect=True, inNamespace=False, **opts): """Convenience method: Add NAT to graph. name: NAT name connect: True will automatically connect to the first switch""" - nat = self.addNode(name, isNAT=True, inNamespace=False) + #nat = self.addNode(name, isNAT=True, inNamespace=False) + nat = self.addNode(name, cls=NAT, inNamespace=inNamespace, hosts=self.hosts(), **opts) if connect: # connect the NAT to the first switch self.addLink(name, self.switches()[ 0 ]) From 555d10dea7232588cb5039a0c6989e197232be40 Mon Sep 17 00:00:00 2001 From: Brian O'Connor Date: Thu, 3 Oct 2013 15:15:40 -0700 Subject: [PATCH 04/10] adding internet / nat example --- examples/natnet.py | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100755 examples/natnet.py diff --git a/examples/natnet.py b/examples/natnet.py new file mode 100755 index 0000000..718ca42 --- /dev/null +++ b/examples/natnet.py @@ -0,0 +1,69 @@ +#!/usr/bin/python + +""" +natnet.py: Example network with NATs + + + h0 + | + s0 + | + ---------------- + | | + nat1 nat2 + | | + s1 s2 + | | + h1 h2 + +""" + +from mininet.topo import Topo +from mininet.net import Mininet +from mininet.node import NAT +from mininet.log import setLogLevel +from mininet.cli import CLI +from mininet.util import irange + +class InternetTopo(Topo): + "Single switch connected to n hosts." + def __init__(self, n=2, h=1, **opts): + Topo.__init__(self, **opts) + + # set up inet switch + inetSwitch = self.addSwitch('s0') + # add inet host + inetHost = self.addHost('h0') + self.addLink(inetSwitch, inetHost) + + # add local nets + for i in irange(1, n): + inetIntf = 'nat%d-eth0' % i + localIntf = 'nat%d-eth1' % i + localIP = '192.168.%d.1' % i + localSubnet = '192.168.%d.0/24' % i + natParams = { 'ip' : '%s/24' % localIP } + # add NAT to topology + nat = self.addNode('nat%d' % i, cls=NAT, subnet=localSubnet, + inetIntf=inetIntf, localIntf=localIntf) + switch = self.addSwitch('s%d' % i) + # connect NAT to inet and local switches + self.addLink(nat, inetSwitch, intfName1=inetIntf) + self.addLink(nat, switch, intfName1=localIntf, params1=natParams) + # add host and connect to local switch + host = self.addHost('h%d' % i, + ip='192.168.%d.100/24' % i, + defaultRoute='via %s' % localIP) + self.addLink(host, switch) + +def perfTest(): + "Create network and run simple performance test" + topo = InternetTopo() + net = Mininet(topo=topo) + net.start() + CLI(net) + net.stop() + +if __name__ == '__main__': + setLogLevel('info') + perfTest() \ No newline at end of file From a802d8b19a5e03f45d733b242ccbc475010471a3 Mon Sep 17 00:00:00 2001 From: Brian O'Connor Date: Thu, 3 Oct 2013 15:19:34 -0700 Subject: [PATCH 05/10] more NAT cleanup of net and topo --- mininet/net.py | 21 --------------------- mininet/topo.py | 12 ------------ 2 files changed, 33 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index 64174fc..f2f49b3 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -181,10 +181,6 @@ class Mininet( object ): self.nextCore = ( self.nextCore + 1 ) % self.numCores self.nextIP += 1 defaults.update( params ) - # TODO: clean this up - if params.get( 'isNAT', False ): - print "***** &&&&&& !!!! nat nat nat" - cls = NAT if not cls: cls = self.host h = cls( name, **defaults ) @@ -319,21 +315,6 @@ class Mininet( object ): host.cmd( 'ifconfig lo up' ) info( '\n' ) - ''' TODO: remove this! - def configGateway( self ): - """Add gateway routes to all hosts if the networks has a gateway.""" - if self.gateway: - gatewayIP = self.gateway.defaultIntf().IP() - for host in self.hosts: - if host.inNamespace and self.gateway: - host.cmd( 'ip route flush root 0/0' ) - host.cmd( 'route add -net', self.ipBase, 'dev', host.defaultIntf() ) - host.cmd( 'route add default gw', gatewayIP ) - else: - # Don't mess with hosts in the root namespace - pass - ''' - def buildFromTopo( self, topo=None ): """Build mininet from a topology object At the end of this function, everything should be connected @@ -392,8 +373,6 @@ class Mininet( object ): self.startTerms() if self.autoStaticArp: self.staticArp() - # TODO: remove this - #self.configGateway() self.built = True def startTerms( self ): diff --git a/mininet/topo.py b/mininet/topo.py index 4ae447c..2ab455c 100644 --- a/mininet/topo.py +++ b/mininet/topo.py @@ -12,7 +12,6 @@ setup for testing, and can even be emulated with the Mininet package. ''' from mininet.util import irange, natural, naturalSeq -from mininet.node import NAT class MultiGraph( object ): "Utility class to track nodes and edges - replaces networkx.Graph" @@ -90,17 +89,6 @@ class Topo(object): result = self.addNode(name, isSwitch=True, **opts) return result - def addNAT(self, name='nat', connect=True, inNamespace=False, **opts): - """Convenience method: Add NAT to graph. - name: NAT name - connect: True will automatically connect to the first switch""" - #nat = self.addNode(name, isNAT=True, inNamespace=False) - nat = self.addNode(name, cls=NAT, inNamespace=inNamespace, hosts=self.hosts(), **opts) - if connect: - # connect the NAT to the first switch - self.addLink(name, self.switches()[ 0 ]) - return nat - def addLink(self, node1, node2, port1=None, port2=None, **opts): """node1, node2: nodes to link together From ffeb16eb66fdd5d0e8b5e48bc0b979ae0472b707 Mon Sep 17 00:00:00 2001 From: Brian O'Connor Date: Thu, 3 Oct 2013 16:29:05 -0700 Subject: [PATCH 06/10] fixing --nat option in mn --- bin/mn | 6 ++++-- examples/natnet.py | 4 ++-- mininet/net.py | 14 +++++++++----- mininet/node.py | 9 +-------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/bin/mn b/bin/mn index 0424d35..474a3c2 100755 --- a/bin/mn +++ b/bin/mn @@ -226,8 +226,6 @@ class MininetRunner( object ): start = time.time() topo = buildTopo( TOPOS, self.options.topo ) - if self.options.nat: - topo.addNAT() switch = customConstructor( SWITCHES, self.options.switch ) host = customConstructor( HOSTS, self.options.host ) controller = customConstructor( CONTROLLERS, self.options.controller ) @@ -255,6 +253,10 @@ class MininetRunner( object ): autoStaticArp=arp, autoPinCpus=pin, listenPort=listenPort ) + if self.options.nat: + nat = mn.addNAT() + mn.configHosts() + if self.options.pre: CLI( mn, script=self.options.pre ) diff --git a/examples/natnet.py b/examples/natnet.py index 718ca42..cb0c44c 100755 --- a/examples/natnet.py +++ b/examples/natnet.py @@ -56,7 +56,7 @@ class InternetTopo(Topo): defaultRoute='via %s' % localIP) self.addLink(host, switch) -def perfTest(): +def run(): "Create network and run simple performance test" topo = InternetTopo() net = Mininet(topo=topo) @@ -66,4 +66,4 @@ def perfTest(): if __name__ == '__main__': setLogLevel('info') - perfTest() \ No newline at end of file + run() \ No newline at end of file diff --git a/mininet/net.py b/mininet/net.py index f2f49b3..1912fba 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -227,14 +227,18 @@ class Mininet( object ): self.nameToNode[ name ] = controller_new return controller_new - # TODO: incomplete - def addNAT( self, name='nat0', connect=True, **params ): - nat = self.addHost( name, cls=NAT, **params ) + def addNAT( self, name='nat0', connect=True, inNamespace=False, **params ): + nat = self.addHost( name, cls=NAT, inNamespace=inNamespace, + subnet=self.ipBase, **params ) # find first switch and create link - print "******* &&&&&& net/addNAT" if connect: - #connect the nat to the first switch + # connect the nat to the first switch self.addLink( nat, self.switches[ 0 ] ) + # set the default route on hosts + natIP = nat.params[ 'ip' ].split('/')[ 0 ] + for host in self.hosts: + if host.inNamespace: + host.setDefaultRoute( 'via %s' % natIP ) return nat # BL: We now have four ways to look up nodes diff --git a/mininet/node.py b/mininet/node.py index d6e8f87..edc6de8 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -1253,7 +1253,7 @@ class NAT( Node ): inetIntf: interface for internet access subnet: Mininet subnet (default 10.0/8)=""" self.inetIntf = inetIntf - self.subnet = subnet #TODO: get subnet from Mininet directly + self.subnet = subnet self.localIntf = localIntf def config( self, **params ): @@ -1263,13 +1263,6 @@ class NAT( Node ): if not self.localIntf: self.localIntf = self.defaultIntf() - #------------------------- - print "inetIntf:", self.inetIntf - print "subnet:", self.subnet - # Identify the interface connecting to the mininet network - print "LocalIntf:", self.localIntf - #------------------------- - self.cmd( 'sysctl net.ipv4.ip_forward=0' ) # Flush any currently active rules From e0af160213e730ff6f0b9b0d33fed3f22304ce1e Mon Sep 17 00:00:00 2001 From: Brian O'Connor Date: Wed, 23 Oct 2013 13:48:50 -0700 Subject: [PATCH 07/10] small fixes for NAT --- bin/mn | 2 +- examples/natnet.py | 4 ++-- mininet/node.py | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bin/mn b/bin/mn index 474a3c2..5bab076 100755 --- a/bin/mn +++ b/bin/mn @@ -255,7 +255,7 @@ class MininetRunner( object ): if self.options.nat: nat = mn.addNAT() - mn.configHosts() + nat.configDefault() if self.options.pre: CLI( mn, script=self.options.pre ) diff --git a/examples/natnet.py b/examples/natnet.py index cb0c44c..7f51fd5 100755 --- a/examples/natnet.py +++ b/examples/natnet.py @@ -26,7 +26,7 @@ from mininet.cli import CLI from mininet.util import irange class InternetTopo(Topo): - "Single switch connected to n hosts." + "TODO: Single switch connected to n hosts." def __init__(self, n=2, h=1, **opts): Topo.__init__(self, **opts) @@ -57,7 +57,7 @@ class InternetTopo(Topo): self.addLink(host, switch) def run(): - "Create network and run simple performance test" + "TODO: Create network and run simple performance test" topo = InternetTopo() net = Mininet(topo=topo) net.start() diff --git a/mininet/node.py b/mininet/node.py index edc6de8..dba383d 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -36,6 +36,8 @@ RemoteController: a remote controller node, which may use any arbitrary OpenFlow-compatible controller, and which is not created or managed by mininet. +TODO: NAT + Future enhancements: - Possibly make Node, Switch and Controller more abstract so that @@ -1309,4 +1311,3 @@ class NAT( Node ): self.cmd( 'sysctl net.ipv4.ip_forward=0' ) super( NAT, self ).terminate() - From cee62eb28ec0aa8c38f3d483b536afde1c62d8ab Mon Sep 17 00:00:00 2001 From: Brian O'Connor Date: Wed, 13 Aug 2014 22:03:25 -0700 Subject: [PATCH 08/10] adding natnet example test --- examples/natnet.py | 6 ++-- examples/test/test_natnet.py | 57 ++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 examples/test/test_natnet.py diff --git a/examples/natnet.py b/examples/natnet.py index 7f51fd5..9fcc4bf 100755 --- a/examples/natnet.py +++ b/examples/natnet.py @@ -7,7 +7,7 @@ natnet.py: Example network with NATs h0 | s0 - | + | ---------------- | | nat1 nat2 @@ -26,7 +26,7 @@ from mininet.cli import CLI from mininet.util import irange class InternetTopo(Topo): - "TODO: Single switch connected to n hosts." + "Single switch connected to n hosts." def __init__(self, n=2, h=1, **opts): Topo.__init__(self, **opts) @@ -57,7 +57,7 @@ class InternetTopo(Topo): self.addLink(host, switch) def run(): - "TODO: Create network and run simple performance test" + "Create network and run the CLI" topo = InternetTopo() net = Mininet(topo=topo) net.start() diff --git a/examples/test/test_natnet.py b/examples/test/test_natnet.py new file mode 100644 index 0000000..3addc92 --- /dev/null +++ b/examples/test/test_natnet.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +""" +Test for natnet.py +""" + +import unittest +import pexpect +from mininet.util import quietRun + +class testNATNet( unittest.TestCase ): + + prompt = 'mininet>' + + def setUp( self ): + self.net = pexpect.spawn( 'python -m mininet.examples.natnet' ) + self.net.expect( self.prompt ) + + def testPublicPing( self ): + "Attempt to ping the public server (h0) from h1 and h2" + self.net.sendline( 'h1 ping -c 1 h0' ) + self.net.expect ( '(\d+)% packet loss' ) + percent = int( self.net.match.group( 1 ) ) if self.net.match else -1 + self.assertEqual( percent, 0 ) + self.net.expect( self.prompt ) + + self.net.sendline( 'h2 ping -c 1 h0' ) + self.net.expect ( '(\d+)% packet loss' ) + percent = int( self.net.match.group( 1 ) ) if self.net.match else -1 + self.assertEqual( percent, 0 ) + self.net.expect( self.prompt ) + + def testPrivatePing( self ): + "Attempt to ping h1 and h2 from public server" + self.net.sendline( 'h0 ping -c 1 -t 1 h1' ) + result = self.net.expect ( [ 'unreachable', 'loss' ] ) + self.assertEqual( result, 0 ) + self.net.expect( self.prompt ) + + self.net.sendline( 'h0 ping -c 1 -t 1 h2' ) + result = self.net.expect ( [ 'unreachable', 'loss' ] ) + self.assertEqual( result, 0 ) + self.net.expect( self.prompt ) + + def testPrivateToPrivatePing( self ): + "Attempt to ping from NAT'ed host h1 to NAT'ed host h2" + self.net.sendline( 'h1 ping -c 1 -t 1 h2' ) + result = self.net.expect ( [ '[Uu]nreachable', 'loss' ] ) + self.assertEqual( result, 0 ) + self.net.expect( self.prompt ) + + def tearDown( self ): + self.net.sendline( 'exit' ) + self.net.wait() + +if __name__ == '__main__': + unittest.main() From 4015e0666e336b312f83842d78645b4d5d4abff6 Mon Sep 17 00:00:00 2001 From: Brian O'Connor Date: Wed, 13 Aug 2014 22:09:24 -0700 Subject: [PATCH 09/10] moving NAT to nodelib --- examples/natnet.py | 2 +- mininet/net.py | 3 +- mininet/node.py | 70 ------------------------------------------- mininet/nodelib.py | 74 ++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 72 insertions(+), 77 deletions(-) diff --git a/examples/natnet.py b/examples/natnet.py index 9fcc4bf..c2fe007 100755 --- a/examples/natnet.py +++ b/examples/natnet.py @@ -20,7 +20,7 @@ natnet.py: Example network with NATs from mininet.topo import Topo from mininet.net import Mininet -from mininet.node import NAT +from mininet.nodelib import NAT from mininet.log import setLogLevel from mininet.cli import CLI from mininet.util import irange diff --git a/mininet/net.py b/mininet/net.py index 42ab0a7..8807804 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -96,7 +96,8 @@ from itertools import chain, groupby from mininet.cli import CLI from mininet.log import info, error, debug, output, warn -from mininet.node import Host, OVSKernelSwitch, DefaultController, Controller, NAT +from mininet.node import Host, OVSKernelSwitch, DefaultController, Controller +from mininet.nodelib import NAT from mininet.link import Link, Intf from mininet.util import quietRun, fixLimits, numCores, ensureRoot from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd diff --git a/mininet/node.py b/mininet/node.py index 4e50fad..fec7117 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -41,8 +41,6 @@ RemoteController: a remote controller node, which may use any arbitrary OpenFlow-compatible controller, and which is not created or managed by mininet. -TODO: NAT - Future enhancements: - Possibly make Node, Switch and Controller more abstract so that @@ -1362,71 +1360,3 @@ def DefaultController( name, order=[ Controller, OVSController ], **kwargs ): for controller in order: if controller.isAvailable(): return controller( name, **kwargs ) - -class NAT( Node ): - """NAT: Provides connectivity to external network""" - - def __init__( self, name, inetIntf='eth0', 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.subnet = subnet - self.localIntf = localIntf - - def config( self, **params ): - super( NAT, self).config( **params ) - """Configure the NAT and iptables""" - - if not self.localIntf: - self.localIntf = self.defaultIntf() - - self.cmd( 'sysctl net.ipv4.ip_forward=0' ) - - # Flush any currently active rules - # TODO: is this safe? - self.cmd( 'iptables -F' ) - self.cmd( 'iptables -t nat -F' ) - - # Create default entries for unmatched traffic - self.cmd( 'iptables -P INPUT ACCEPT' ) - self.cmd( 'iptables -P OUTPUT ACCEPT' ) - self.cmd( 'iptables -P FORWARD DROP' ) - - # Configure NAT - 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' ) - - # Instruct the kernel to perform forwarding - self.cmd( 'sysctl net.ipv4.ip_forward=1' ) - - # Prevent network-manager from messing with our interface - # by specifying manual configuration in /etc/network/interfaces - intf = self.localIntf - cfile = '/etc/network/interfaces' - line = '\niface %s inet manual\n' % intf - config = open( cfile ).read() - if ( line ) not in config: - info( '*** Adding "' + line.strip() + '" to ' + cfile ) - 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 terminate( self ): - """Stop NAT/forwarding between Mininet and external network""" - # Flush any currently active rules - # TODO: is this safe? - self.cmd( 'iptables -F' ) - self.cmd( 'iptables -t nat -F' ) - - # Instruct the kernel to stop forwarding - self.cmd( 'sysctl net.ipv4.ip_forward=0' ) - - super( NAT, self ).terminate() - diff --git a/mininet/nodelib.py b/mininet/nodelib.py index 2eb8046..1760c7b 100644 --- a/mininet/nodelib.py +++ b/mininet/nodelib.py @@ -1,15 +1,12 @@ """ Node Library for Mininet -This contains additional Node types which you may find to be useful +This contains additional Node types which you may find to be useful. """ -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.node import Switch +from mininet.node import Node, Switch from mininet.log import setLogLevel, info - class LinuxBridge( Switch ): "Linux Bridge (with optional spanning tree)" @@ -49,3 +46,70 @@ class LinuxBridge( Switch ): self.cmd( 'ifconfig', self, 'down' ) self.cmd( 'brctl delbr', self ) +class NAT( Node ): + """NAT: Provides connectivity to external network""" + + def __init__( self, name, inetIntf='eth0', 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.subnet = subnet + self.localIntf = localIntf + + def config( self, **params ): + super( NAT, self).config( **params ) + """Configure the NAT and iptables""" + + if not self.localIntf: + self.localIntf = self.defaultIntf() + + self.cmd( 'sysctl net.ipv4.ip_forward=0' ) + + # Flush any currently active rules + # TODO: is this safe? + self.cmd( 'iptables -F' ) + self.cmd( 'iptables -t nat -F' ) + + # Create default entries for unmatched traffic + self.cmd( 'iptables -P INPUT ACCEPT' ) + self.cmd( 'iptables -P OUTPUT ACCEPT' ) + self.cmd( 'iptables -P FORWARD DROP' ) + + # Configure NAT + 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' ) + + # Instruct the kernel to perform forwarding + self.cmd( 'sysctl net.ipv4.ip_forward=1' ) + + # Prevent network-manager from messing with our interface + # by specifying manual configuration in /etc/network/interfaces + intf = self.localIntf + cfile = '/etc/network/interfaces' + line = '\niface %s inet manual\n' % intf + config = open( cfile ).read() + if ( line ) not in config: + info( '*** Adding "' + line.strip() + '" to ' + cfile ) + 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 terminate( self ): + """Stop NAT/forwarding between Mininet and external network""" + # Flush any currently active rules + # TODO: is this safe? + self.cmd( 'iptables -F' ) + self.cmd( 'iptables -t nat -F' ) + + # Instruct the kernel to stop forwarding + self.cmd( 'sysctl net.ipv4.ip_forward=0' ) + + super( NAT, self ).terminate() + From 7c4e5b14cbfd4b09e768f3619330fe9c37e8ef0c Mon Sep 17 00:00:00 2001 From: Brian O'Connor Date: Thu, 14 Aug 2014 01:07:44 -0700 Subject: [PATCH 10/10] adding line to natnet.py --- examples/natnet.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/natnet.py b/examples/natnet.py index c2fe007..4305d1f 100755 --- a/examples/natnet.py +++ b/examples/natnet.py @@ -66,4 +66,5 @@ def run(): if __name__ == '__main__': setLogLevel('info') - run() \ No newline at end of file + run() + \ No newline at end of file