You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
5.7 KiB
Python
155 lines
5.7 KiB
Python
"""
|
|
Node Library for Mininet
|
|
|
|
This contains additional Node types which you may find to be useful.
|
|
"""
|
|
|
|
from mininet.node import Node, Switch
|
|
from mininet.log import info, warn
|
|
from mininet.moduledeps import pathCheck
|
|
from mininet.util import quietRun
|
|
|
|
|
|
class LinuxBridge( Switch ):
|
|
"Linux Bridge (with optional spanning tree)"
|
|
|
|
nextPrio = 100 # next bridge priority for spanning tree
|
|
|
|
def __init__( self, name, stp=False, prio=None, **kwargs ):
|
|
"""stp: use spanning tree protocol? (default False)
|
|
prio: optional explicit bridge priority for STP"""
|
|
self.stp = stp
|
|
if prio:
|
|
self.prio = prio
|
|
else:
|
|
self.prio = LinuxBridge.nextPrio
|
|
LinuxBridge.nextPrio += 1
|
|
Switch.__init__( self, name, **kwargs )
|
|
|
|
def connected( self ):
|
|
"Are we forwarding yet?"
|
|
if self.stp:
|
|
return 'forwarding' in self.cmd( 'brctl showstp', self )
|
|
else:
|
|
return True
|
|
|
|
def start( self, _controllers ):
|
|
"Start Linux bridge"
|
|
self.cmd( 'ifconfig', self, 'down' )
|
|
self.cmd( 'brctl delbr', self )
|
|
self.cmd( 'brctl addbr', self )
|
|
if self.stp:
|
|
self.cmd( 'brctl setbridgeprio', self.prio )
|
|
self.cmd( 'brctl stp', self, 'on' )
|
|
for i in self.intfList():
|
|
if self.name in i.name:
|
|
self.cmd( 'brctl addif', self, i )
|
|
self.cmd( 'ifconfig', self, 'up' )
|
|
|
|
def stop( self, deleteIntfs=True ):
|
|
"""Stop Linux bridge
|
|
deleteIntfs: delete interfaces? (True)"""
|
|
self.cmd( 'ifconfig', self, 'down' )
|
|
self.cmd( 'brctl delbr', self )
|
|
super( LinuxBridge, self ).stop( deleteIntfs )
|
|
|
|
def dpctl( self, *args ):
|
|
"Run brctl command"
|
|
return self.cmd( 'brctl', *args )
|
|
|
|
@classmethod
|
|
def setup( cls ):
|
|
"Check dependencies and warn about firewalling"
|
|
pathCheck( 'brctl', moduleName='bridge-utils' )
|
|
# Disable Linux bridge firewalling so that traffic can flow!
|
|
for table in 'arp', 'ip', 'ip6':
|
|
cmd = 'sysctl net.bridge.bridge-nf-call-%stables' % table
|
|
out = quietRun( cmd ).strip()
|
|
if out.endswith( '1' ):
|
|
warn( 'Warning: Linux bridge may not work with', out, '\n' )
|
|
|
|
|
|
class NAT( Node ):
|
|
"NAT: Provides connectivity to external network"
|
|
|
|
def __init__( self, name, subnet='10.0/8',
|
|
localIntf=None, flush=False, **params):
|
|
"""Start NAT/forwarding between Mininet and external network
|
|
subnet: Mininet subnet (default 10.0/8)
|
|
flush: flush iptables before installing NAT rules"""
|
|
super( NAT, self ).__init__( name, **params )
|
|
|
|
self.subnet = subnet
|
|
self.localIntf = localIntf
|
|
self.flush = flush
|
|
self.forwardState = self.cmd( 'sysctl -n net.ipv4.ip_forward' ).strip()
|
|
|
|
def setManualConfig( self, intf ):
|
|
"""Prevent network-manager/networkd from messing with our interface
|
|
by specifying manual configuration in /etc/network/interfaces"""
|
|
cfile = '/etc/network/interfaces'
|
|
line = '\niface %s inet manual\n' % intf
|
|
try:
|
|
with open( cfile ) as f:
|
|
config = f.read()
|
|
except IOError:
|
|
config = ''
|
|
if ( line ) not in config:
|
|
info( '*** Adding "' + line.strip() + '" to ' + cfile + '\n' )
|
|
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 || netplan apply' )
|
|
|
|
# pylint: disable=arguments-differ
|
|
def config( self, **params ):
|
|
"""Configure the NAT and iptables"""
|
|
|
|
if not self.localIntf:
|
|
self.localIntf = self.defaultIntf()
|
|
|
|
self.setManualConfig( self.localIntf )
|
|
|
|
# Now we can configure manually without interference
|
|
super( NAT, self).config( **params )
|
|
|
|
if self.flush:
|
|
self.cmd( 'sysctl net.ipv4.ip_forward=0' )
|
|
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' )
|
|
|
|
# Install NAT rules
|
|
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',
|
|
'-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' )
|
|
self.cmd( 'iptables -t nat -A POSTROUTING',
|
|
'-s', self.subnet, "'!'", '-d', self.subnet,
|
|
'-j MASQUERADE' )
|
|
|
|
# Instruct the kernel to perform forwarding
|
|
self.cmd( 'sysctl net.ipv4.ip_forward=1' )
|
|
|
|
def terminate( self ):
|
|
"Stop NAT/forwarding between Mininet and external network"
|
|
# Remote NAT rules
|
|
self.cmd( 'iptables -D FORWARD',
|
|
'-i', self.localIntf, '-d', self.subnet, '-j DROP' )
|
|
self.cmd( 'iptables -D FORWARD',
|
|
'-i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
|
|
self.cmd( 'iptables -D FORWARD',
|
|
'-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' )
|
|
self.cmd( 'iptables -t nat -D POSTROUTING',
|
|
'-s', self.subnet, '\'!\'', '-d', self.subnet,
|
|
'-j MASQUERADE' )
|
|
# Put the forwarding state back to what it was
|
|
self.cmd( 'sysctl net.ipv4.ip_forward=%s' % self.forwardState )
|
|
super( NAT, self ).terminate()
|