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): ...@@ -20,7 +20,7 @@ Or with the user switch (or CPqD if installed):
20 mn --custom onos.py --controller onos,3 \ 20 mn --custom onos.py --controller onos,3 \
21 --switch onosuser --topo torus,4,4 21 --switch onosuser --topo torus,4,4
22 22
23 -Currently you meed to specify a custom switch class 23 +Currently you meed to use a custom switch class
24 because Mininet's Switch() class does't (yet?) handle 24 because Mininet's Switch() class does't (yet?) handle
25 controllers with multiple IP addresses directly. 25 controllers with multiple IP addresses directly.
26 26
...@@ -59,7 +59,9 @@ import time ...@@ -59,7 +59,9 @@ import time
59 59
60 ### ONOS Environment 60 ### ONOS Environment
61 61
62 -KarafPort = 8101 # ssh port indicating karaf is running 62 +KarafPort = 8101 # ssh port indicating karaf is running
63 +GUIPort = 8181 # GUI/REST port
64 +OpenFlowPort = 6653 # OpenFlow port
63 65
64 def defaultUser(): 66 def defaultUser():
65 "Return a reasonable default user" 67 "Return a reasonable default user"
...@@ -71,7 +73,6 @@ def defaultUser(): ...@@ -71,7 +73,6 @@ def defaultUser():
71 user = 'nobody' 73 user = 'nobody'
72 return user 74 return user
73 75
74 -
75 # Module vars, initialized below 76 # Module vars, initialized below
76 HOME = ONOS_ROOT = KARAF_ROOT = ONOS_HOME = ONOS_USER = None 77 HOME = ONOS_ROOT = KARAF_ROOT = ONOS_HOME = ONOS_USER = None
77 ONOS_APPS = ONOS_WEB_USER = ONOS_WEB_PASS = ONOS_TAR = None 78 ONOS_APPS = ONOS_WEB_USER = ONOS_WEB_PASS = ONOS_TAR = None
...@@ -239,13 +240,13 @@ class ONOSNode( Controller ): ...@@ -239,13 +240,13 @@ class ONOSNode( Controller ):
239 info( '.' ) 240 info( '.' )
240 time.sleep( 1 ) 241 time.sleep( 1 )
241 info( ' ssh-port' ) 242 info( ' ssh-port' )
242 - waitListening( client=self, server=self, port=8101 ) 243 + waitListening( server=self, port=KarafPort )
243 info( ' openflow-port' ) 244 info( ' openflow-port' )
244 - waitListening( server=self, port=6653 ) 245 + waitListening( server=self, port=OpenFlowPort )
245 info( ' client' ) 246 info( ' client' )
246 while True: 247 while True:
247 - result = quietRun( 'echo apps -a | %s -h %s' % ( self.client, self.IP() ), 248 + result = quietRun( 'echo apps -a | %s -h %s' %
248 - shell=True ) 249 + ( self.client, self.IP() ), shell=True )
249 if 'openflow' in result: 250 if 'openflow' in result:
250 break 251 break
251 info( '.' ) 252 info( '.' )
...@@ -265,6 +266,7 @@ class ONOSCluster( Controller ): ...@@ -265,6 +266,7 @@ class ONOSCluster( Controller ):
265 """name: (first parameter) 266 """name: (first parameter)
266 *args: topology class parameters 267 *args: topology class parameters
267 ipBase: IP range for ONOS nodes 268 ipBase: IP range for ONOS nodes
269 + forward: default port forwarding list,
268 topo: topology class or instance 270 topo: topology class or instance
269 **kwargs: additional topology parameters""" 271 **kwargs: additional topology parameters"""
270 args = list( args ) 272 args = list( args )
...@@ -277,11 +279,13 @@ class ONOSCluster( Controller ): ...@@ -277,11 +279,13 @@ class ONOSCluster( Controller ):
277 args = ( 1, ) 279 args = ( 1, )
278 if not isinstance( topo, Topo ): 280 if not isinstance( topo, Topo ):
279 topo = RenamedTopo( topo, *args, hnew='onos', **kwargs ) 281 topo = RenamedTopo( topo, *args, hnew='onos', **kwargs )
280 - ipBase = kwargs.pop( 'ipBase', '192.168.123.0/24' ) 282 + self.ipBase = kwargs.pop( 'ipBase', '192.168.123.0/24' )
283 + self.forward = kwargs.pop( 'forward',
284 + [ KarafPort, GUIPort, OpenFlowPort ] )
281 super( ONOSCluster, self ).__init__( name, inNamespace=False ) 285 super( ONOSCluster, self ).__init__( name, inNamespace=False )
282 fixIPTables() 286 fixIPTables()
283 self.env = initONOSEnv() 287 self.env = initONOSEnv()
284 - self.net = Mininet( topo=topo, ipBase=ipBase, 288 + self.net = Mininet( topo=topo, ipBase=self.ipBase,
285 host=ONOSNode, switch=LinuxBridge, 289 host=ONOSNode, switch=LinuxBridge,
286 controller=None ) 290 controller=None )
287 self.net.addNAT().configDefault() 291 self.net.addNAT().configDefault()
...@@ -296,6 +300,7 @@ class ONOSCluster( Controller ): ...@@ -296,6 +300,7 @@ class ONOSCluster( Controller ):
296 for node in self.nodes(): 300 for node in self.nodes():
297 node.start( self.env ) 301 node.start( self.env )
298 info( '\n' ) 302 info( '\n' )
303 + self.configPortForwarding( ports=self.forward, action='A' )
299 self.waitStarted() 304 self.waitStarted()
300 return 305 return
301 306
...@@ -305,10 +310,12 @@ class ONOSCluster( Controller ): ...@@ -305,10 +310,12 @@ class ONOSCluster( Controller ):
305 for node in self.nodes(): 310 for node in self.nodes():
306 info( node ) 311 info( node )
307 node.waitStarted() 312 node.waitStarted()
308 - info( '*** Waited %.2f seconds for ONOS startup' % ( time.time() - startTime ) ) 313 + info( '*** Waited %.2f seconds for ONOS startup' %
314 + ( time.time() - startTime ) )
309 315
310 def stop( self ): 316 def stop( self ):
311 "Shut down ONOS cluster" 317 "Shut down ONOS cluster"
318 + self.configPortForwarding( ports=self.forward, action='D' )
312 for node in self.nodes(): 319 for node in self.nodes():
313 node.stop() 320 node.stop()
314 self.net.stop() 321 self.net.stop()
...@@ -317,6 +324,18 @@ class ONOSCluster( Controller ): ...@@ -317,6 +324,18 @@ class ONOSCluster( Controller ):
317 "Return list of ONOS nodes" 324 "Return list of ONOS nodes"
318 return [ h for h in self.net.hosts if isinstance( h, ONOSNode ) ] 325 return [ h for h in self.net.hosts if isinstance( h, ONOSNode ) ]
319 326
327 + def configPortForwarding( self, ports=[], intf='eth0', action='A' ):
328 + """Start or stop ports on intf to all nodes
329 + action: A=add/start, D=delete/stop (default: A)"""
330 + for port in ports:
331 + for index, node in enumerate( self.nodes() ):
332 + ip, inport = node.IP(), port + index
333 + # Configure a destination NAT rule
334 + cmd = ( 'iptables -t nat -{action} PREROUTING -t nat '
335 + '-i {intf} -p tcp --dport {inport} '
336 + '-j DNAT --to-destination {ip}:{port}' )
337 + self.cmd( cmd.format( **locals() ) )
338 +
320 339
321 class ONOSSwitchMixin( object ): 340 class ONOSSwitchMixin( object ):
322 "Mixin for switches that connect to an ONOSCluster" 341 "Mixin for switches that connect to an ONOSCluster"
...@@ -413,6 +432,9 @@ switches = { 'onos': ONOSOVSSwitch, ...@@ -413,6 +432,9 @@ switches = { 'onos': ONOSOVSSwitch,
413 'onosuser': ONOSUserSwitch, 432 'onosuser': ONOSUserSwitch,
414 'default': ONOSOVSSwitch } 433 'default': ONOSOVSSwitch }
415 434
435 +# Null topology so we can control an external/hardware network
436 +topos = { 'none': Topo }
437 +
416 if __name__ == '__main__': 438 if __name__ == '__main__':
417 if len( argv ) != 2: 439 if len( argv ) != 2:
418 test( 3 ) 440 test( 3 )
......