Adding NAT class
Includes automatic NAT feature (mn --nat) and addNAT convenience method for topologies fixes #111
This commit is contained in:
committed by
Brian O'Connor
parent
433d83221a
commit
47b9466fad
@@ -193,6 +193,9 @@ class MininetRunner( object ):
|
|||||||
opts.add_option( '--pin', action='store_true',
|
opts.add_option( '--pin', action='store_true',
|
||||||
default=False, help="pin hosts to CPU cores "
|
default=False, help="pin hosts to CPU cores "
|
||||||
"(requires --host cfs or --host rt)" )
|
"(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 )
|
opts.add_option( '--version', action='callback', callback=version )
|
||||||
|
|
||||||
self.options, self.args = opts.parse_args()
|
self.options, self.args = opts.parse_args()
|
||||||
@@ -223,6 +226,8 @@ class MininetRunner( object ):
|
|||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
topo = buildTopo( TOPOS, self.options.topo )
|
topo = buildTopo( TOPOS, self.options.topo )
|
||||||
|
if self.options.nat:
|
||||||
|
topo.addNAT()
|
||||||
switch = customConstructor( SWITCHES, self.options.switch )
|
switch = customConstructor( SWITCHES, self.options.switch )
|
||||||
host = customConstructor( HOSTS, self.options.host )
|
host = customConstructor( HOSTS, self.options.host )
|
||||||
controller = customConstructor( CONTROLLERS, self.options.controller )
|
controller = customConstructor( CONTROLLERS, self.options.controller )
|
||||||
|
|||||||
+39
-4
@@ -95,7 +95,7 @@ from itertools import chain
|
|||||||
|
|
||||||
from mininet.cli import CLI
|
from mininet.cli import CLI
|
||||||
from mininet.log import info, error, debug, output
|
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.link import Link, Intf
|
||||||
from mininet.util import quietRun, fixLimits, numCores, ensureRoot
|
from mininet.util import quietRun, fixLimits, numCores, ensureRoot
|
||||||
from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd
|
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',
|
build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
|
||||||
inNamespace=False,
|
inNamespace=False,
|
||||||
autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
|
autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
|
||||||
listenPort=None ):
|
listenPort=None,
|
||||||
|
gateway=None ):
|
||||||
"""Create Mininet object.
|
"""Create Mininet object.
|
||||||
topo: Topo (topology) object or None
|
topo: Topo (topology) object or None
|
||||||
switch: default Switch class
|
switch: default Switch class
|
||||||
@@ -129,7 +130,8 @@ class Mininet( object ):
|
|||||||
autoStaticArp: set all-pairs static MAC addrs?
|
autoStaticArp: set all-pairs static MAC addrs?
|
||||||
autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)?
|
autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)?
|
||||||
listenPort: base listening port to open; will be incremented for
|
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.topo = topo
|
||||||
self.switch = switch
|
self.switch = switch
|
||||||
self.host = host
|
self.host = host
|
||||||
@@ -148,6 +150,7 @@ class Mininet( object ):
|
|||||||
self.numCores = numCores()
|
self.numCores = numCores()
|
||||||
self.nextCore = 0 # next core for pinning hosts to CPUs
|
self.nextCore = 0 # next core for pinning hosts to CPUs
|
||||||
self.listenPort = listenPort
|
self.listenPort = listenPort
|
||||||
|
self.gateway = gateway
|
||||||
|
|
||||||
self.hosts = []
|
self.hosts = []
|
||||||
self.switches = []
|
self.switches = []
|
||||||
@@ -163,6 +166,9 @@ class Mininet( object ):
|
|||||||
if topo and build:
|
if topo and build:
|
||||||
self.build()
|
self.build()
|
||||||
|
|
||||||
|
# list of Node subclasses that provide gateway service
|
||||||
|
gateways = [ NAT ]
|
||||||
|
|
||||||
def addHost( self, name, cls=None, **params ):
|
def addHost( self, name, cls=None, **params ):
|
||||||
"""Add host.
|
"""Add host.
|
||||||
name: name of host to add
|
name: name of host to add
|
||||||
@@ -175,17 +181,22 @@ class Mininet( object ):
|
|||||||
prefixLen=self.prefixLen ) +
|
prefixLen=self.prefixLen ) +
|
||||||
'/%s' % self.prefixLen }
|
'/%s' % self.prefixLen }
|
||||||
if self.autoSetMacs:
|
if self.autoSetMacs:
|
||||||
defaults[ 'mac'] = macColonHex( self.nextIP )
|
defaults[ 'mac' ] = macColonHex( self.nextIP )
|
||||||
if self.autoPinCpus:
|
if self.autoPinCpus:
|
||||||
defaults[ 'cores' ] = self.nextCore
|
defaults[ 'cores' ] = self.nextCore
|
||||||
self.nextCore = ( self.nextCore + 1 ) % self.numCores
|
self.nextCore = ( self.nextCore + 1 ) % self.numCores
|
||||||
self.nextIP += 1
|
self.nextIP += 1
|
||||||
defaults.update( params )
|
defaults.update( params )
|
||||||
|
# TODO: clean this up
|
||||||
|
if params.get( 'isNAT', False ):
|
||||||
|
cls = NAT
|
||||||
if not cls:
|
if not cls:
|
||||||
cls = self.host
|
cls = self.host
|
||||||
h = cls( name, **defaults )
|
h = cls( name, **defaults )
|
||||||
self.hosts.append( h )
|
self.hosts.append( h )
|
||||||
self.nameToNode[ name ] = h
|
self.nameToNode[ name ] = h
|
||||||
|
if cls in self.gateways:
|
||||||
|
self.gateway = h
|
||||||
return h
|
return h
|
||||||
|
|
||||||
def addSwitch( self, name, cls=None, **params ):
|
def addSwitch( self, name, cls=None, **params ):
|
||||||
@@ -227,6 +238,16 @@ class Mininet( object ):
|
|||||||
self.nameToNode[ name ] = controller_new
|
self.nameToNode[ name ] = controller_new
|
||||||
return 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
|
# BL: We now have four ways to look up nodes
|
||||||
# This may (should?) be cleaned up in the future.
|
# This may (should?) be cleaned up in the future.
|
||||||
def getNodeByName( self, *args ):
|
def getNodeByName( self, *args ):
|
||||||
@@ -305,6 +326,19 @@ class Mininet( object ):
|
|||||||
host.cmd( 'ifconfig lo up' )
|
host.cmd( 'ifconfig lo up' )
|
||||||
info( '\n' )
|
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 ):
|
def buildFromTopo( self, topo=None ):
|
||||||
"""Build mininet from a topology object
|
"""Build mininet from a topology object
|
||||||
At the end of this function, everything should be connected
|
At the end of this function, everything should be connected
|
||||||
@@ -363,6 +397,7 @@ class Mininet( object ):
|
|||||||
self.startTerms()
|
self.startTerms()
|
||||||
if self.autoStaticArp:
|
if self.autoStaticArp:
|
||||||
self.staticArp()
|
self.staticArp()
|
||||||
|
self.configGateway()
|
||||||
self.built = True
|
self.built = True
|
||||||
|
|
||||||
def startTerms( self ):
|
def startTerms( self ):
|
||||||
|
|||||||
@@ -1243,3 +1243,67 @@ class RemoteController( Controller ):
|
|||||||
warn( "Unable to contact the remote controller"
|
warn( "Unable to contact the remote controller"
|
||||||
" at %s:%d\n" % ( self.ip, self.port ) )
|
" 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()
|
||||||
|
|||||||
@@ -89,6 +89,16 @@ class Topo(object):
|
|||||||
result = self.addNode(name, isSwitch=True, **opts)
|
result = self.addNode(name, isSwitch=True, **opts)
|
||||||
return result
|
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,
|
def addLink(self, node1, node2, port1=None, port2=None,
|
||||||
**opts):
|
**opts):
|
||||||
"""node1, node2: nodes to link together
|
"""node1, node2: nodes to link together
|
||||||
|
|||||||
Reference in New Issue
Block a user