intergrate ns3 with mininet

 In my last blog[1], I intend to change link bandwidth dynamically in mininet. Even the method works, it still complains some error. So, I want to send the packets in ns3 for further processing, in which the delay and bandwidth of a link is easily manipulated. I follow the method in [2] and get it running.
 This code can be used to test multipath protocols (MPTCP, QUIC)
 Make some minor change in node.py.

--- a/mininet/node.py
+++ b/mininet/node.py
@@ -423,8 +423,11 @@ def addIntf( self, intf, port=None, moveIntfFn=moveIntf ):
         debug( 'added intf %s (%d) to node %s\n' % (
                 intf, port, self.name ) )
         if self.inNamespace:
-            debug( 'moving', intf, 'into namespace for', self.name, '\n' )
-            moveIntfFn( intf.name, self  )
+            if hasattr( intf, 'delayedMove' ) and intf.delayedMove is True:
+                pass
+            else:
+                debug( 'moving', intf, 'into namespace for', self.name, '\n' )
+                moveIntfFn( intf.name, self  )

     def defaultIntf( self ):
         "Return interface for lowest port"

 Then rebuild mininet.

mininet/util/install.sh

 Test code:
myns3.py

"""
NS-3 integration for Mininet.
 Mininet                                               Mininet
 node 1                                                node 2
+---------+                                           +---------+
| name    |                                           | name    |
| space 1 |                                           | space 2 |
| ------- |                                           |---------|
| shell   |                                           | shell   |
| ------- |                                           |---------|
| Linux   |                                           | Linux   |
| network |                                           | network |
| stack   |       ns-3              ns-3              | stack   |
| ------  |       node 1            node 2            |---------|
| TAP     |      |===========|     |===========|      | TAP     |
| intf.   |<-fd->| TapBridge |     | TapBridge |<-fd->| intf.   |
+---------+      | --------- |     | --------- |      +---------+
                 | ns-3      |     | ns-3      |
                 | net       |     | net       |
                 | device    |     | device    |
                 +-----------+     +-----------+
                       ||               ||
                  +---------------------------+
                  |        ns-3 channel       |
                  +---------------------------+
               |<------------------------------->|
                           ns-3 process
                      in the root namespace
"""

import threading, time

from mininet.log import info, error, warn, debug
from mininet.link import Intf, Link
from mininet.node import Switch, Node
from mininet.util import quietRun, moveIntf, errRun

import ns.core
import ns.network
import ns.tap_bridge
import ns.csma
import ns.wifi
import ns.mobility

# Default duration of ns-3 simulation thread. You can freely modify this value.

default_duration = 3600

# Set ns-3 simulator type to realtime simulator implementation.
# You can find more information about realtime modes here:
# http://www.nsnam.org/docs/release/3.17/manual/singlehtml/index.html#realtime
# http://www.nsnam.org/wiki/index.php/Emulation_and_Realtime_Scheduler

ns.core.GlobalValue.Bind( "SimulatorImplementationType", ns.core.StringValue( "ns3::RealtimeSimulatorImpl" ) )

# Enable checksum computation in ns-3 devices. By default ns-3 does not compute checksums - it is not needed
# when it runs in simulation mode. However, when it runs in emulation mode and exchanges packets with the real
# world, bit errors may occur in the real world, so we need to enable checksum computation.

ns.core.GlobalValue.Bind( "ChecksumEnabled", ns.core.BooleanValue ( "true" ) )

# Arrays which track all created TBIntf objects and Mininet nodes which has assigned an underlying ns-3 node.
#ns.core.LogComponentEnable("TapBridge", ns.core.LOG_LEVEL_INFO)
allTBIntfs = []
allNodes = []

# These four global functions below are used to control ns-3 simulator thread. They are global, because
# ns-3 has one global singleton simulator object.

def start():
    """ Start the simulator thread in background.
        It should be called after configuration of all ns-3 objects
        (TBintfs, Segments and Links).
        Attempt of adding an ns-3 object when simulator thread is
        running may result in segfault. You should stop it first."""
    global thread
    if 'thread' in globals() and thread.isAlive():
        warn( "NS-3 simulator thread already running." )
        return
    # Install all TapBridge ns-3 devices not installed yet.
    for intf in allTBIntfs:
        if not intf.nsInstalled:
            intf.nsInstall()
    # Set up the simulator thread.
    thread = threading.Thread( target = runthread )
    thread.daemon = True
    # Start the simulator thread (this is where fork happens).
    # FORK!
    thread.start()
    # FORK:PARENT
    # Code below is executed in the parent thread.
    # Move all tap interfaces not moved yet to the right namespace.
    for intf in allTBIntfs:
        if not intf.inRightNamespace:
            intf.namespaceMove()
    return

def runthread():
    """ Method called in the simulator thread on its start.
        Should not be called manually."""
    # FORK:CHILD
    # Code below is executed in the simulator thread after the fork.
    # Stop event must be scheduled before simulator start. Not scheduling it
    # may lead leads to segfault.
    ns.core.Simulator.Stop( ns.core.Seconds( default_duration ) )
    # Start simulator. Function below blocks the Python thread and returns when simulator stops.
    ns.core.Simulator.Run()

def stop():
    """ Stop the simulator thread now."""
    # Schedule a stop event.
    ns.core.Simulator.Stop( ns.core.MilliSeconds( 1 ) )
    # Wait until the simulator thread stops.
    while thread.isAlive():
        time.sleep( 0.01 )
    return

def clear():
    """ Clear ns-3 simulator.
        It should be called when simulator is stopped."""
    ns.core.Simulator.Destroy()
    for intf in allTBIntfs:
        intf.nsInstalled = False
        intf.delete()
    for node in allNodes:
        del node.nsNode
    del allTBIntfs[:]
    del allNodes[:]
    return

# Functions for manipulating nodes positions. Nodes positioning is useful in
# wireless channel simulations: distance between nodes affects received signal power
# and, thus, throughput.
# Node positions are stored in the underlying ns-3 node (not in Mininet node itself).

def getPosition( node ):
    """ Return the ns-3 (x, y, z) position of a Mininet node.
        Coordinates are in the 3D Cartesian system.
        The unit is meters.
        node: Mininet node"""
    # Check if this Mininet node has assigned the underlying ns-3 node.
    if hasattr( node, 'nsNode' ) and node.nsNode is not None:
        # If it is assigned, go ahead.
        pass
    else:
        # If not, create new ns-3 node and assign it to this Mininet node.
        node.nsNode = ns.network.Node()
        allNodes.append( node )
    try:
        # Get postion coordinates from the ns-3 node
        mm = node.nsNode.GetObject( ns.mobility.MobilityModel.GetTypeId() )
        pos = mm.GetPosition()
        return ( pos.x, pos.y, pos.z )
    except AttributeError:
        warn( "ns-3 mobility model not found\n" )
        return ( 0, 0, 0 )

def setPosition( node, x, y, z ):
    """ Set the ns-3 (x, y, z) position of a Mininet node.
        Coordinates are in the 3D Cartesian system.
        The unit is meters.
        node: Mininet node
        x: integer or float x coordinate
        y: integer or float y coordinate
        z: integer or float z coordinate"""
    # Check if this Mininet node has assigned the underlying ns-3 node.
    if hasattr( node, 'nsNode' ) and node.nsNode is not None:
        # If it is assigned, go ahead.
        pass
    else:
        # If not, create new ns-3 node and assign it to this Mininet node.
        node.nsNode = ns.network.Node()
        allNodes.append( node )
    try:
        mm = node.nsNode.GetObject( ns.mobility.MobilityModel.GetTypeId() )
        if z is None:
            z = 0.0
        # Set postion coordinates in the ns-3 node
        pos = mm.SetPosition( ns.core.Vector( x, y, z ) )
    except AttributeError:
        warn( "ns-3 mobility model not found, not setting position\n" )

# TBIntf is the main workhorse of the module. TBIntf is a tap Linux interface located on Mininet
# node, which is bridged with ns-3 device located on ns-3 node.
ip_addr="255.255.255."
mac_addr="93:85:45:6e:e5:"
ip_count=1
mac_count=1
class TBIntf( Intf ):
    """ Interface object that is bridged with ns-3 emulated device.
        This is a subclass of Mininet basic Inft object. """

    def __init__( self, name, node, port=None,
                  nsNode=None, nsDevice=None, mode=None, **params ):
        """name: interface name (e.g. h1-eth0)
           node: owning Mininet node (where this intf most likely lives)
           link: parent link if we're part of a link #TODO
           nsNode: underlying ns-3 node
           nsDevice: ns-3 device which the tap interface is bridged with
           mode: mode of TapBridge ns-3 device (UseLocal or UseBridge)
           other arguments are passed to config()"""
        self.name = name
        # Create a tap interface in the system, ns-3 TapBridge will connect to that interface later.
        self.createTap()
        # Set this Intf to be delayed move. This tells Mininet not to move the interface to the right
        # namespace during Intf.__init__(). Therefore, the interface must be moved manually later.
        # Actually, interfaces are moved right after the simulator thread start, in the start() global
        # function.
        self.delayedMove = True
        # If this node is running in its own namespace...
        if node.inNamespace:
            # ...this interface is not yet in the right namespace (it is in the root namespace just after
            # creation) and should be moved later.
            self.inRightNamespace = False
        else:
            # ...interface should stay in the root namespace, so it is in right namespace now.
            self.inRightNamespace = True
        # Initialize parent Intf object.
        Intf.__init__( self, name, node, port , **params)
        allTBIntfs.append( self )
        self.nsNode = nsNode
        self.nsDevice = nsDevice
        self.mode = mode
        self.params = params
        self.nsInstalled = False
        # Create TapBridge ns-3 device.
        self.tapbridge = ns.tap_bridge.TapBridge()
        # If ns-3 node and bridged ns-3 device are set and TapBridge mode is known...
        if self.nsNode and self.nsDevice and ( self.mode or self.node ):
            # ...call nsInstall().
            self.nsInstall()
    def eth_name(self):
        return self.name
    def createTap( self ):
        """Create tap Linux interface in the root namespace."""
        quietRun( 'ip tuntap add ' + self.name + ' mode tap' )

    def nsInstall( self ):
        """Install TapBridge ns-3 device in the ns-3 simulator."""
        if not isinstance( self.nsNode, ns.network.Node ):
            warn( "Cannot install TBIntf to ns-3 Node: "
                  "nsNode not specified\n" )
            return
        if not isinstance( self.nsDevice, ns.network.NetDevice ):
            warn( "Cannot install TBIntf to ns-3 Node: "
                  "nsDevice not specified\n" )
            return
        # If TapBridge mode has not been set explicitly, determine it automatically basing on
        # a Mininet node type. You can find more about TapBridge modes there:
        # http://www.nsnam.org/docs/release/3.18/models/singlehtml/index.html#tap-netdevice
        if self.mode is None and self.node is not None:
            # If Mininet node is some kind of Switch...
            if isinstance( self.node, Switch ):
                # ...use "UseBridge" mode. In this mode there may be many different L2 devices with
                # many source addresses on the Linux side of TapBridge, but bridged ns-3 device must
                # support SendFrom().
                self.mode = "UseBridge"
            else:
                # ...in the other case use "UseLocal" mode. In this mode there may be only one L2 source device
                # on the Linux side of TapBridge (TapBridge will change source MAC address of all packets coming
                # from the tap interface to the discovered address of this interface). In this mode bridged ns-3
                # device does not have to support SendFrom() (it uses Send() function to send packets).
                self.mode = "UseLocal"
        if self.mode is None:
            warn( "Cannot install TBIntf to ns-3 Node: "
                  "cannot determine mode: neither mode nor (mininet) node specified\n" )
            return
        # Set all required TapBridge attributes.
        #self.mode="ConfigureLocal"
        self.tapbridge.SetAttribute ( "Mode", ns.core.StringValue( self.mode ) )
        self.tapbridge.SetAttribute ( "DeviceName", ns.core.StringValue( self.name ) )
        #global ip_count
        #global mac_count
        #self.tapbridge.SetAttribute ( "IpAddress", ns.core.StringValue( ip_addr+str(ip_count)) )
        #self.tapbridge.SetAttribute ( "MacAddress", ns.core.StringValue( mac_addr+str(mac_count) ) )
        #ip_count+=1
        #mac_count+=1
        # Add TapBridge device to the ns-3 node.
        self.nsNode.AddDevice( self.tapbridge )
        # Set this TapBridge to be bridged with the specified ns-3 device.
        self.tapbridge.SetBridgedNetDevice( self.nsDevice )
        # Installation is done.
        self.nsInstalled = True

    def namespaceMove( self ):
        """Move tap Linux interface to the right namespace."""
        loops = 0
        # Wait until ns-3 process connects to the tap Linux interface. ns-3 process resides in the root
        # network namespace, so it must manage to connect to the interface before it is moved to the node
        # namespace. After interface move ns-3 process will not see the interface.
        while not self.isConnected():
            time.sleep( 0.01 )
            loops += 1
            if loops > 10:
                warn( "Cannot move TBIntf to mininet Node namespace: "
                      "ns-3 has not connected yet to the TAP interface\n" )
                return
        # Wait a little more, just for be sure ns-3 process not miss that.
        time.sleep( 0.1 )
        # Move interface to the right namespace.
        moveIntf( self.name, self.node )
        self.inRightNamespace = True
        # IP address has been reset while moving to namespace, needs to be set again.
        if self.ip is not None:
            self.setIP( self.ip, self.prefixLen )
        # The same for 'up'.
        self.isUp( True )

    def isConnected( self ):
        """Check if ns-3 TapBridge has connected to the Linux tap interface."""
        return self.tapbridge.IsLinkUp()

    def cmd( self, *args, **kwargs ):
        "Run a command in our owning node namespace or in the root namespace when not yet inRightNamespace."
        if self.inRightNamespace:
            return self.node.cmd( *args, **kwargs )
        else:
            cmd = ' '.join( [ str( c ) for c in args ] )
            return errRun( cmd )[ 0 ]

    def rename( self, newname ):
        "Rename interface"
        # If TapBridge is installed in ns-3, but ns-3 has not connected to the Linux tap interface yet...
        if self.nsInstalled and not self.isConnected():
            # ...change device name in TapBridge to the new one.
            self.tapbridge.SetAttribute ( "DeviceName", ns.core.StringValue( newname ) )
        Intf.rename( self, newname )

    def delete( self ):
        "Delete interface"
        if self.nsInstalled:
            warn( "You can not delete once installed ns-3 device, "
                  "run mininet.ns3.clear() to delete all ns-3 devices\n" )
        else:
            Intf.delete( self )

# Network segment is a Mininet object consistng of ns-3 channel of a specific type. This can be seen as
# an equivalent of collision domain. Many Mininet nodes can be connected to the one network segment.
# During connecting, Mininet creates ns-3 device of particular type in the underlying  ns-3 node.
# Then it connects this ns-3 device to the segment's ns-3 channel. Next, Mininet creates TBIntf in the
# specified Mininet node and bridges this tap interface with the ns-3 device created formerly.

# Network link is a subclass of network segment. It is a network segment with only two nodes connected.
# Moreover, network link is a subclass of Mininet Link. It means that you can use it like standard Mininet
# Link and alternatively with it: it supports all methods of its superclass and constructor arguments order
# is the same.

# SimpleChannel is the simplest channel model available in ns-3. Many devices can be connected to it
# simultaneously. Devices supports SendFrom(), therefore it can be used in "UseBridge" mode (for example
# for connecting switches). There is no implemented channel blocking - many devices can transmit
# simultaneously. Data rate and channel delay can not be set. However, one can
# set the receiver error model in SimpleNetDevice to simulate packet loss. You can find more information
# about the SimpleChannel in its source code and here:
# http://www.nsnam.org/docs/doxygen/classns3_1_1_simple_channel.html

class SimpleSegment( object ):
    """The simplest channel model available in ns-3.
       SimpleNetDevice supports SendFrom()."""
    def __init__( self,rate,delay):
        self.channel = ns.network.SimpleChannel()
        self.channel.SetAttribute("Delay", ns.core.TimeValue(ns.core.MilliSeconds (delay)))
        self.rate=rate
    def add( self, node, port=None, intfName=None, mode=None ):
        """Connect Mininet node to the segment.
           node: Mininet node
           port: node port number (optional)
           intfName: node tap interface name (optional)
           mode: TapBridge mode (UseLocal or UseBridge) (optional)"""
        # Check if this Mininet node has assigned an underlying ns-3 node.
        if hasattr( node, 'nsNode' ) and node.nsNode is not None:
            # If it is assigned, go ahead.
            pass
        else:
            # If not, create new ns-3 node and assign it to this Mininet node.
            node.nsNode = ns.network.Node()
            allNodes.append( node )
        # Create ns-3 device.
        device = ns.network.SimpleNetDevice()
        # Connect this device to the segment's channel.
        device.SetChannel(self.channel)
        device.SetAttribute("DataRate",ns.network.DataRateValue(ns.network.DataRate(self.rate)))
        # Add this device to the ns-3 node.
        node.nsNode.AddDevice(device)
        # If port number is not specified...
        if port is None:
            # ...obtain it automatically.
            port = node.newPort()
        # If interface name is not specified...
        if intfName is None:
            # ...obtain it automatically.
            ##intfName = Link.intfName( node, port ) # classmethod
            ##https://github.com/Barthurmun/NS3-Mininet/blob/master/mininet-patch/mininet/ns3.py
            intfName = node.name + '-eth' + repr( port )
        # In the specified Mininet node, create TBIntf bridged with the 'device'.
        tb = TBIntf( intfName, node, port, node.nsNode, device, mode )
        return tb


class SimpleLink( SimpleSegment, Link):
    """Link between two nodes using the SimpleChannel ns-3 model"""
    def __init__( self, node1, node2, rate,delay,port1=None, port2=None,
                  intfName1=None, intfName2=None ):
        """Create simple link to another node, making two new tap interfaces.
        node1: first Mininet node
        node2: second Mininet node
        port1: node1 port number (optional)
        port2: node2 port number (optional)
        intfName1: node1 interface name (optional)
        intfName2: node2 interface name (optional)"""
        SimpleSegment.__init__( self,rate,delay)
        intf1 = SimpleSegment.add( self, node1, port1, intfName1 )
        intf2 = SimpleSegment.add( self, node2, port2, intfName2 )
        intf1.link = self
        intf2.link = self
        self.intf1, self.intf2 = intf1, intf2
    def intf1_name(self):
        return self.intf1.eth_name()
    def intf2_name(self):
        return self.intf2.eth_name()
    def interface1(self):
        return self.intf1
    def interface2(self):
        return self.intf2       

simple-link.py

from mininet.net import Mininet
from mininet.node import Node, Switch
from mininet.link import Link, Intf
from mininet.log import setLogLevel, info
from mininet.cli import CLI
import time
import myns3
from myns3 import SimpleLink

##./waf shell
## python simple-link
##aborted. cond="bytesWritten != p->GetSize ()", msg="TapBridge::ReceiveFromBridgedDevice(): Write error.", 
##file=../src/tap-bridge/model/tap-bridge.cc, line=993 terminate called without an active exception
if __name__ == '__main__':
    setLogLevel( 'info' )
    info( '*** ns-3 network demo\n' )
    net = Mininet()

    info( '*** Creating Network\n' )
    h0 = Node( 'h0' )
    h1 = Node( 'h1' )

    net.hosts.append( h0 )
    net.hosts.append( h1 )
    bw=2000000
    link = SimpleLink( h0, h1,bw,10)   
    link1 = SimpleLink( h0, h1,bw,40 )
    myns3.start()

    info( '*** Configuring hosts\n' )
    print link.intf1_name(),link1.intf1_name()
    ip1='192.168.123.1'
    ip2='192.168.123.2'
    h0.setIP( ip1+'/24',intf=link.interface1())
    h1.setIP( ip2+'/24',intf=link.interface2())

    ip3='192.168.124.3'
    ip4='192.168.124.4'
    h0.setIP( ip3+'/24',intf=link1.interface1())
    h1.setIP( ip4+'/24',intf=link1.interface2())
    info( '*** Network state:\n' )
    for node in h0, h1:
        info( str( node ) + '\n' )

    info( '*** Running test\n' )
    h0.cmdPrint( 'ping -I'+ip1+' -c1 '+ ip2)
    h0.cmdPrint( 'ping -I'+ip3+' -c1 '+ ip4)
    time.sleep(1)
    h0.cmdPrint( 'ping -I'+ip1+' -c1 '+ ip2)
    h0.cmdPrint( 'ping -I'+ip3+' -c1 '+ ip4)
    h0.cmdPrint( 'ping -I'+ip1+' -c1 '+ ip2)
    h0.cmdPrint( 'ping -I'+ip3+' -c1 '+ ip4)
    #CLI(net)
    time.sleep(20)
    myns3.stop()

 Put simple-link.py and myns3.py under ns-allinone-3.2x/ns-3.2x.
 The topology in simple-link.py:

  h0-eth0-------h1-eth0
 /                    \
h0                    h1
 \                    /
  h0-eth1-------h1-eth1

  h0-eth0 and h0-eth1 should be configured with ip addresses in different subnets, or else there will be wrong results. Fuck it, this costs me one day!
 Run:

sudo su
./waf shell
./waf --pyrun  simple-link.py
mn -c

Reference
[1] change link bandwidth dynamically in mininet
[1] NS3-Mininet

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值