Adding NAT class

Includes automatic NAT feature (mn --nat) and addNAT convenience method for topologies
fixes #111
This commit is contained in:
Brian O'Connor
2013-10-03 09:39:40 -07:00
committed by Brian O'Connor
parent 433d83221a
commit 47b9466fad
4 changed files with 118 additions and 4 deletions
+5
View File
@@ -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 )
+39 -4
View File
@@ -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 ):
+64
View File
@@ -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()
+10
View File
@@ -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