Bob Lantz
Committed by Bob Lantz

Add port forwarding using iptables

By default, we now forward the following ports from
port on eth0 to port+N on onosN:

8101 (KarafPort)
8181 (GUIPort) (also REST)
6643 (OpenFlowPort)

Note: this will not work if your default interface is
called something other than eth0.

Also: added null topology so we can use onos.py to control
an external/hardware network.

Bugs: it seems that iptables isn't cleaned up completely -
Mininet's NAT element may be to blame.

Change-Id: I2197194100a77ebfddd0d38ad5194ad7569ceba3
......@@ -20,7 +20,7 @@ Or with the user switch (or CPqD if installed):
mn --custom onos.py --controller onos,3 \
--switch onosuser --topo torus,4,4
Currently you meed to specify a custom switch class
Currently you meed to use a custom switch class
because Mininet's Switch() class does't (yet?) handle
controllers with multiple IP addresses directly.
......@@ -59,7 +59,9 @@ import time
### ONOS Environment
KarafPort = 8101 # ssh port indicating karaf is running
KarafPort = 8101 # ssh port indicating karaf is running
GUIPort = 8181 # GUI/REST port
OpenFlowPort = 6653 # OpenFlow port
def defaultUser():
"Return a reasonable default user"
......@@ -71,7 +73,6 @@ def defaultUser():
user = 'nobody'
return user
# Module vars, initialized below
HOME = ONOS_ROOT = KARAF_ROOT = ONOS_HOME = ONOS_USER = None
ONOS_APPS = ONOS_WEB_USER = ONOS_WEB_PASS = ONOS_TAR = None
......@@ -239,13 +240,13 @@ class ONOSNode( Controller ):
info( '.' )
time.sleep( 1 )
info( ' ssh-port' )
waitListening( client=self, server=self, port=8101 )
waitListening( server=self, port=KarafPort )
info( ' openflow-port' )
waitListening( server=self, port=6653 )
waitListening( server=self, port=OpenFlowPort )
info( ' client' )
while True:
result = quietRun( 'echo apps -a | %s -h %s' % ( self.client, self.IP() ),
shell=True )
result = quietRun( 'echo apps -a | %s -h %s' %
( self.client, self.IP() ), shell=True )
if 'openflow' in result:
break
info( '.' )
......@@ -265,6 +266,7 @@ class ONOSCluster( Controller ):
"""name: (first parameter)
*args: topology class parameters
ipBase: IP range for ONOS nodes
forward: default port forwarding list,
topo: topology class or instance
**kwargs: additional topology parameters"""
args = list( args )
......@@ -277,11 +279,13 @@ class ONOSCluster( Controller ):
args = ( 1, )
if not isinstance( topo, Topo ):
topo = RenamedTopo( topo, *args, hnew='onos', **kwargs )
ipBase = kwargs.pop( 'ipBase', '192.168.123.0/24' )
self.ipBase = kwargs.pop( 'ipBase', '192.168.123.0/24' )
self.forward = kwargs.pop( 'forward',
[ KarafPort, GUIPort, OpenFlowPort ] )
super( ONOSCluster, self ).__init__( name, inNamespace=False )
fixIPTables()
self.env = initONOSEnv()
self.net = Mininet( topo=topo, ipBase=ipBase,
self.net = Mininet( topo=topo, ipBase=self.ipBase,
host=ONOSNode, switch=LinuxBridge,
controller=None )
self.net.addNAT().configDefault()
......@@ -296,6 +300,7 @@ class ONOSCluster( Controller ):
for node in self.nodes():
node.start( self.env )
info( '\n' )
self.configPortForwarding( ports=self.forward, action='A' )
self.waitStarted()
return
......@@ -305,10 +310,12 @@ class ONOSCluster( Controller ):
for node in self.nodes():
info( node )
node.waitStarted()
info( '*** Waited %.2f seconds for ONOS startup' % ( time.time() - startTime ) )
info( '*** Waited %.2f seconds for ONOS startup' %
( time.time() - startTime ) )
def stop( self ):
"Shut down ONOS cluster"
self.configPortForwarding( ports=self.forward, action='D' )
for node in self.nodes():
node.stop()
self.net.stop()
......@@ -317,6 +324,18 @@ class ONOSCluster( Controller ):
"Return list of ONOS nodes"
return [ h for h in self.net.hosts if isinstance( h, ONOSNode ) ]
def configPortForwarding( self, ports=[], intf='eth0', action='A' ):
"""Start or stop ports on intf to all nodes
action: A=add/start, D=delete/stop (default: A)"""
for port in ports:
for index, node in enumerate( self.nodes() ):
ip, inport = node.IP(), port + index
# Configure a destination NAT rule
cmd = ( 'iptables -t nat -{action} PREROUTING -t nat '
'-i {intf} -p tcp --dport {inport} '
'-j DNAT --to-destination {ip}:{port}' )
self.cmd( cmd.format( **locals() ) )
class ONOSSwitchMixin( object ):
"Mixin for switches that connect to an ONOSCluster"
......@@ -413,6 +432,9 @@ switches = { 'onos': ONOSOVSSwitch,
'onosuser': ONOSUserSwitch,
'default': ONOSOVSSwitch }
# Null topology so we can control an external/hardware network
topos = { 'none': Topo }
if __name__ == '__main__':
if len( argv ) != 2:
test( 3 )
......