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

"""
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()