First commit
diff --git a/mininet/net.py b/mininet/net.py
new file mode 100644
index 0000000..1255e12
--- /dev/null
+++ b/mininet/net.py
@@ -0,0 +1,770 @@
+"""
+
+    Mininet: A simple networking testbed for OpenFlow/SDN!
+
+author: Bob Lantz (rlantz@cs.stanford.edu)
+author: Brandon Heller (brandonh@stanford.edu)
+
+Mininet creates scalable OpenFlow test networks by using
+process-based virtualization and network namespaces.
+
+Simulated hosts are created as processes in separate network
+namespaces. This allows a complete OpenFlow network to be simulated on
+top of a single Linux kernel.
+
+Each host has:
+
+A virtual console (pipes to a shell)
+A virtual interfaces (half of a veth pair)
+A parent shell (and possibly some child processes) in a namespace
+
+Hosts have a network interface which is configured via ifconfig/ip
+link/etc.
+
+This version supports both the kernel and user space datapaths
+from the OpenFlow reference implementation (openflowswitch.org)
+as well as OpenVSwitch (openvswitch.org.)
+
+In kernel datapath mode, the controller and switches are simply
+processes in the root namespace.
+
+Kernel OpenFlow datapaths are instantiated using dpctl(8), and are
+attached to the one side of a veth pair; the other side resides in the
+host namespace. In this mode, switch processes can simply connect to the
+controller via the loopback interface.
+
+In user datapath mode, the controller and switches can be full-service
+nodes that live in their own network namespaces and have management
+interfaces and IP addresses on a control network (e.g. 192.168.123.1,
+currently routed although it could be bridged.)
+
+In addition to a management interface, user mode switches also have
+several switch interfaces, halves of veth pairs whose other halves
+reside in the host nodes that the switches are connected to.
+
+Consistent, straightforward naming is important in order to easily
+identify hosts, switches and controllers, both from the CLI and
+from program code. Interfaces are named to make it easy to identify
+which interfaces belong to which node.
+
+The basic naming scheme is as follows:
+
+    Host nodes are named h1-hN
+    Switch nodes are named s1-sN
+    Controller nodes are named c0-cN
+    Interfaces are named {nodename}-eth0 .. {nodename}-ethN
+
+Note: If the network topology is created using mininet.topo, then
+node numbers are unique among hosts and switches (e.g. we have
+h1..hN and SN..SN+M) and also correspond to their default IP addresses
+of 10.x.y.z/8 where x.y.z is the base-256 representation of N for
+hN. This mapping allows easy determination of a node's IP
+address from its name, e.g. h1 -> 10.0.0.1, h257 -> 10.0.1.1.
+
+Note also that 10.0.0.1 can often be written as 10.1 for short, e.g.
+"ping 10.1" is equivalent to "ping 10.0.0.1".
+
+Currently we wrap the entire network in a 'mininet' object, which
+constructs a simulated network based on a network topology created
+using a topology object (e.g. LinearTopo) from mininet.topo or
+mininet.topolib, and a Controller which the switches will connect
+to. Several configuration options are provided for functions such as
+automatically setting MAC addresses, populating the ARP table, or
+even running a set of terminals to allow direct interaction with nodes.
+
+After the network is created, it can be started using start(), and a
+variety of useful tasks maybe performed, including basic connectivity
+and bandwidth tests and running the mininet CLI.
+
+Once the network is up and running, test code can easily get access
+to host and switch objects which can then be used for arbitrary
+experiments, typically involving running a series of commands on the
+hosts.
+
+After all desired tests or activities have been completed, the stop()
+method may be called to shut down the network.
+
+"""
+
+import os
+import re
+import select
+import signal
+from time import sleep
+
+from mininet.cli import CLI
+from mininet.log import info, error, debug, output
+from mininet.node import Host, OVSKernelSwitch, Controller
+from mininet.link import Link, Intf
+from mininet.util import quietRun, fixLimits, numCores, ensureRoot
+from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd, nextCCNnet
+from mininet.term import cleanUpScreens, makeTerms
+
+# Mininet version: should be consistent with README and LICENSE
+VERSION = "2.0.0"
+
+class Mininet( object ):
+    "Network emulation with hosts spawned in network namespaces."
+
+    def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
+                  controller=Controller, link=Link, intf=Intf,
+                  build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
+                  inNamespace=False,
+                  autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
+                  listenPort=None ):
+        """Create Mininet object.
+           topo: Topo (topology) object or None
+           switch: default Switch class
+           host: default Host class/constructor
+           controller: default Controller class/constructor
+           link: default Link class/constructor
+           intf: default Intf class/constructor
+           ipBase: base IP address for hosts,
+           build: build now from topo?
+           xterms: if build now, spawn xterms?
+           cleanup: if build now, cleanup before creating?
+           inNamespace: spawn switches and controller in net namespaces?
+           autoSetMacs: set MAC addrs automatically like IP addresses?
+           autoStaticArp: set all-pairs static MAC addrs?
+           autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)?
+           listenPort: base listening port to open; will be incremented for
+               each additional switch in the net if inNamespace=False"""
+        self.topo = topo
+        self.switch = switch
+        self.host = host
+        self.controller = controller
+        self.link = link
+        self.intf = intf
+        self.ipBase = ipBase
+        self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
+        self.nextIP = 1  # start for address allocation
+        self.ccnNetBase = '1.0.0.0'
+        self.inNamespace = inNamespace
+        self.xterms = xterms
+        self.cleanup = cleanup
+        self.autoSetMacs = autoSetMacs
+        self.autoStaticArp = autoStaticArp
+        self.autoPinCpus = autoPinCpus
+        self.numCores = numCores()
+        self.nextCore = 0  # next core for pinning hosts to CPUs
+        self.listenPort = listenPort
+
+        self.hosts = []
+        self.switches = []
+        self.controllers = []
+
+        self.nameToNode = {}  # name to Node (Host/Switch) objects
+
+        self.terms = []  # list of spawned xterm processes
+
+        Mininet.init()  # Initialize Mininet if necessary
+
+        self.built = False
+        if topo and build:
+            self.build()
+
+    def isCCNhost(self, node):
+        if 'fib' in node.params:
+            return True
+        else:
+            return False
+        
+    def addHost( self, name, cls=None, **params ):
+        """Add host.
+           name: name of host to add
+           cls: custom host class/constructor (optional)
+           params: parameters for host
+           returns: added host"""
+        # Default IP and MAC addresses
+
+        defaults = { 'ip': ipAdd( self.nextIP,
+                                  ipBaseNum=self.ipBaseNum,
+                                  prefixLen=self.prefixLen ) +
+                                  '/%s' % self.prefixLen }
+        if self.autoSetMacs:
+            defaults[ 'mac'] = macColonHex( self.nextIP )
+        if self.autoPinCpus:
+            defaults[ 'cores' ] = self.nextCore
+            self.nextCore = ( self.nextCore + 1 ) % self.numCores        
+        self.nextIP += 1
+        defaults.update( params )
+        if not cls:
+            cls = self.host
+        h = cls( name, **defaults )
+        self.hosts.append( h )
+        self.nameToNode[ name ] = h
+        return h
+
+    def addSwitch( self, name, cls=None, **params ):
+        """Add switch.
+           name: name of switch to add
+           cls: custom switch class/constructor (optional)
+           returns: added switch
+           side effect: increments listenPort ivar ."""
+        defaults = { 'listenPort': self.listenPort,
+                     'inNamespace': self.inNamespace }
+        defaults.update( params )
+        if not cls:
+            cls = self.switch
+        sw = cls( name, **defaults )
+        if not self.inNamespace and self.listenPort:
+            self.listenPort += 1
+        self.switches.append( sw )
+        self.nameToNode[ name ] = sw
+        return sw
+
+    def addController( self, name='c0', controller=None, **params ):
+        """Add controller.
+           controller: Controller class"""
+        if not controller:
+            controller = self.controller
+        controller_new = controller( name, **params )
+        if controller_new:  # allow controller-less setups
+            self.controllers.append( controller_new )
+            self.nameToNode[ name ] = controller_new
+        return controller_new
+
+    # BL: is this better than just using nameToNode[] ?
+    # Should it have a better name?
+    def getNodeByName( self, *args ):
+        "Return node(s) with given name(s)"
+        if len( args ) == 1:
+            return self.nameToNode[ args[ 0 ] ]
+        return [ self.nameToNode[ n ] for n in args ]
+
+    def get( self, *args ):
+        "Convenience alias for getNodeByName"
+        return self.getNodeByName( *args )
+
+    def addLink( self, node1, node2, port1=None, port2=None,
+                 cls=None, **params ):
+        """"Add a link from node1 to node2
+            node1: source node
+            node2: dest node
+            port1: source port
+            port2: dest port
+            returns: link object"""
+        defaults = { 'port1': port1,
+                     'port2': port2,
+                     'intf': self.intf }
+        defaults.update( params )
+        if not cls:
+            cls = self.link
+        return cls( node1, node2, **defaults )
+
+    def configHosts( self ):
+        "Configure a set of hosts."
+        for host in self.hosts:
+            info( host.name + ' ' )
+            intf = host.defaultIntf()
+            if self.isCCNhost(host):
+                host.configCCN()    
+                host.configDefault(ip=None,mac=None)         
+            elif intf:
+                host.configDefault( defaultRoute=intf )
+            else:
+                # Don't configure nonexistent intf
+                host.configDefault( ip=None, mac=None )
+            # You're low priority, dude!
+            # BL: do we want to do this here or not?
+            # May not make sense if we have CPU lmiting...
+            # quietRun( 'renice +18 -p ' + repr( host.pid ) )
+            # This may not be the right place to do this, but
+            # it needs to be done somewhere.
+            host.cmd( 'ifconfig lo up' )
+        info( '\n' )
+
+    def buildFromTopo( self, topo=None ):
+        """Build mininet from a topology object
+           At the end of this function, everything should be connected
+           and up."""
+
+        # Possibly we should clean up here and/or validate
+        # the topo
+        if self.cleanup:
+            pass
+
+        info( '*** Creating network\n' )
+
+        if not self.controllers:
+            # Add a default controller
+            info( '*** Adding controller\n' )
+            classes = self.controller
+            if type( classes ) is not list:
+                classes = [ classes ]
+            for i, cls in enumerate( classes ):
+                self.addController( 'c%d' % i, cls )
+
+        info( '*** Adding hosts:\n' )
+        for hostName in topo.hosts():
+            self.addHost( hostName, **topo.nodeInfo( hostName ) )
+            info( hostName + ' ' )
+
+        info( '\n*** Adding switches:\n' )
+        for switchName in topo.switches():
+            self.addSwitch( switchName, **topo.nodeInfo( switchName) )
+            info( switchName + ' ' )
+
+        info( '\n*** Adding links:\n' )
+        for srcName, dstName in topo.links(sort=True):
+            src, dst = self.nameToNode[ srcName ], self.nameToNode[ dstName ]
+            params = topo.linkInfo( srcName, dstName )
+            srcPort, dstPort = topo.port( srcName, dstName )
+            self.addLink( src, dst, srcPort, dstPort, **params )
+            if self.isCCNhost(src):
+                src.setIP(ipStr(ipParse(self.ccnNetBase) + 1) + '/30', intf=src.name + '-eth' + str(srcPort))
+                dst.setIP(ipStr(ipParse(self.ccnNetBase) + 2) + '/30', intf=dst.name + '-eth' + str(dstPort))
+                self.ccnNetBase=nextCCNnet(self.ccnNetBase)
+                
+            info( '(%s, %s) ' % ( src.name, dst.name ) )
+
+        info( '\n' )
+        
+        
+    def configureControlNetwork( self ):
+        "Control net config hook: override in subclass"
+        raise Exception( 'configureControlNetwork: '
+                         'should be overriden in subclass', self )
+
+    def build( self ):
+        "Build mininet."
+        if self.topo:
+            self.buildFromTopo( self.topo )
+        if ( self.inNamespace ):
+            self.configureControlNetwork()
+        info( '*** Configuring hosts\n' )
+        self.configHosts()
+        if self.xterms:
+            self.startTerms()
+        if self.autoStaticArp:
+            self.staticArp()
+        self.built = True
+
+    def startTerms( self ):
+        "Start a terminal for each node."
+        info( "*** Running terms on %s\n" % os.environ[ 'DISPLAY' ] )
+        cleanUpScreens()
+        self.terms += makeTerms( self.controllers, 'controller' )
+        self.terms += makeTerms( self.switches, 'switch' )
+        self.terms += makeTerms( self.hosts, 'host' )
+
+    def stopXterms( self ):
+        "Kill each xterm."
+        for term in self.terms:
+            os.kill( term.pid, signal.SIGKILL )
+        cleanUpScreens()
+
+    def staticArp( self ):
+        "Add all-pairs ARP entries to remove the need to handle broadcast."
+        for src in self.hosts:
+            for dst in self.hosts:
+                if src != dst:
+                    src.setARP( ip=dst.IP(), mac=dst.MAC() )
+
+    def start( self ):
+        "Start controller and switches."
+        if not self.built:
+            self.build()
+        info( '*** Starting controller\n' )
+        for controller in self.controllers:
+            controller.start()
+        info( '*** Starting %s switches\n' % len( self.switches ) )
+        for switch in self.switches:
+            info( switch.name + ' ')
+            switch.start( self.controllers )
+        info( '\n' )
+
+    def stop( self ):
+        "Stop the controller(s), switches and hosts"
+        if self.terms:
+            info( '*** Stopping %i terms\n' % len( self.terms ) )
+            self.stopXterms()
+        info( '*** Stopping %i hosts\n' % len( self.hosts ) )
+        for host in self.hosts:
+            info( host.name + ' ' )
+            host.terminate()
+        info( '\n' )
+        info( '*** Stopping %i switches\n' % len( self.switches ) )
+        for switch in self.switches:
+            info( switch.name + ' ' )
+            switch.stop()
+        info( '\n' )
+        info( '*** Stopping %i controllers\n' % len( self.controllers ) )
+        for controller in self.controllers:
+            info( controller.name + ' ' )
+            controller.stop()
+        info( '\n*** Done\n' )
+
+    def run( self, test, *args, **kwargs ):
+        "Perform a complete start/test/stop cycle."
+        self.start()
+        info( '*** Running test\n' )
+        result = test( *args, **kwargs )
+        self.stop()
+        return result
+
+    def monitor( self, hosts=None, timeoutms=-1 ):
+        """Monitor a set of hosts (or all hosts by default),
+           and return their output, a line at a time.
+           hosts: (optional) set of hosts to monitor
+           timeoutms: (optional) timeout value in ms
+           returns: iterator which returns host, line"""
+        if hosts is None:
+            hosts = self.hosts
+        poller = select.poll()
+        Node = hosts[ 0 ]  # so we can call class method fdToNode
+        for host in hosts:
+            poller.register( host.stdout )
+        while True:
+            ready = poller.poll( timeoutms )
+            for fd, event in ready:
+                host = Node.fdToNode( fd )
+                if event & select.POLLIN:
+                    line = host.readline()
+                    if line is not None:
+                        yield host, line
+            # Return if non-blocking
+            if not ready and timeoutms >= 0:
+                yield None, None
+
+    # XXX These test methods should be moved out of this class.
+    # Probably we should create a tests.py for them
+
+    @staticmethod
+    def _parsePing( pingOutput ):
+        "Parse ping output and return packets sent, received."
+        # Check for downed link
+        if 'connect: Network is unreachable' in pingOutput:
+            return (1, 0)
+        r = r'(\d+) packets transmitted, (\d+) received'
+        m = re.search( r, pingOutput )
+        if m is None:
+            error( '*** Error: could not parse ping output: %s\n' %
+                   pingOutput )
+            return (1, 0)
+        sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
+        return sent, received
+
+    def ping( self, hosts=None, timeout=None ):
+        """Ping between all specified hosts.
+           hosts: list of hosts
+           timeout: time to wait for a response, as string
+           returns: ploss packet loss percentage"""
+        # should we check if running?
+        packets = 0
+        lost = 0
+        ploss = None
+        if not hosts:
+            hosts = self.hosts
+            output( '*** Ping: testing ping reachability\n' )
+        for node in hosts:
+            output( '%s -> ' % node.name )
+            for dest in hosts:
+                if node != dest:
+                    opts = ''
+                    if timeout:
+                        opts = '-W %s' % timeout
+                    result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
+                    sent, received = self._parsePing( result )
+                    packets += sent
+                    if received > sent:
+                        error( '*** Error: received too many packets' )
+                        error( '%s' % result )
+                        node.cmdPrint( 'route' )
+                        exit( 1 )
+                    lost += sent - received
+                    output( ( '%s ' % dest.name ) if received else 'X ' )
+            output( '\n' )
+            ploss = 100 * lost / packets
+        output( "*** Results: %i%% dropped (%d/%d lost)\n" %
+                ( ploss, lost, packets ) )
+        return ploss
+
+    @staticmethod
+    def _parsePingFull( pingOutput ):
+        "Parse ping output and return all data."
+        # Check for downed link
+        if 'connect: Network is unreachable' in pingOutput:
+            return (1, 0)
+        r = r'(\d+) packets transmitted, (\d+) received'
+        m = re.search( r, pingOutput )
+        if m is None:
+            error( '*** Error: could not parse ping output: %s\n' %
+                   pingOutput )
+            return (1, 0, 0, 0, 0, 0)
+        sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
+        r = r'rtt min/avg/max/mdev = '
+        r += r'(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+) ms'
+        m = re.search( r, pingOutput )
+        rttmin = float( m.group( 1 ) )
+        rttavg = float( m.group( 2 ) )
+        rttmax = float( m.group( 3 ) )
+        rttdev = float( m.group( 4 ) )
+        return sent, received, rttmin, rttavg, rttmax, rttdev
+
+    def pingFull( self, hosts=None, timeout=None ):
+        """Ping between all specified hosts and return all data.
+           hosts: list of hosts
+           timeout: time to wait for a response, as string
+           returns: all ping data; see function body."""
+        # should we check if running?
+        # Each value is a tuple: (src, dsd, [all ping outputs])
+        all_outputs = []
+        if not hosts:
+            hosts = self.hosts
+            output( '*** Ping: testing ping reachability\n' )
+        for node in hosts:
+            output( '%s -> ' % node.name )
+            for dest in hosts:
+                if node != dest:
+                    opts = ''
+                    if timeout:
+                        opts = '-W %s' % timeout
+                    result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
+                    outputs = self._parsePingFull( result )
+                    sent, received, rttmin, rttavg, rttmax, rttdev = outputs
+                    all_outputs.append( (node, dest, outputs) )
+                    output( ( '%s ' % dest.name ) if received else 'X ' )
+            output( '\n' )
+        output( "*** Results: \n" )
+        for outputs in all_outputs:
+            src, dest, ping_outputs = outputs
+            sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
+            output( " %s->%s: %s/%s, " % (src, dest, sent, received ) )
+            output( "rtt min/avg/max/mdev %0.3f/%0.3f/%0.3f/%0.3f ms\n" %
+                    (rttmin, rttavg, rttmax, rttdev) )
+        return all_outputs
+
+    def pingAll( self ):
+        """Ping between all hosts.
+           returns: ploss packet loss percentage"""
+        return self.ping()
+
+    def pingPair( self ):
+        """Ping between first two hosts, useful for testing.
+           returns: ploss packet loss percentage"""
+        hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
+        return self.ping( hosts=hosts )
+
+    def pingAllFull( self ):
+        """Ping between all hosts.
+           returns: ploss packet loss percentage"""
+        return self.pingFull()
+
+    def pingPairFull( self ):
+        """Ping between first two hosts, useful for testing.
+           returns: ploss packet loss percentage"""
+        hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
+        return self.pingFull( hosts=hosts )
+
+    @staticmethod
+    def _parseIperf( iperfOutput ):
+        """Parse iperf output and return bandwidth.
+           iperfOutput: string
+           returns: result string"""
+        r = r'([\d\.]+ \w+/sec)'
+        m = re.findall( r, iperfOutput )
+        if m:
+            return m[-1]
+        else:
+            # was: raise Exception(...)
+            error( 'could not parse iperf output: ' + iperfOutput )
+            return ''
+
+    # XXX This should be cleaned up
+
+    def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ):
+        """Run iperf between two hosts.
+           hosts: list of hosts; if None, uses opposite hosts
+           l4Type: string, one of [ TCP, UDP ]
+           returns: results two-element array of server and client speeds"""
+        if not quietRun( 'which telnet' ):
+            error( 'Cannot find telnet in $PATH - required for iperf test' )
+            return
+        if not hosts:
+            hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ]
+        else:
+            assert len( hosts ) == 2
+        client, server = hosts
+        output( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
+        output( "%s and %s\n" % ( client.name, server.name ) )
+        server.cmd( 'killall -9 iperf' )
+        iperfArgs = 'iperf '
+        bwArgs = ''
+        if l4Type == 'UDP':
+            iperfArgs += '-u '
+            bwArgs = '-b ' + udpBw + ' '
+        elif l4Type != 'TCP':
+            raise Exception( 'Unexpected l4 type: %s' % l4Type )
+        server.sendCmd( iperfArgs + '-s', printPid=True )
+        servout = ''
+        while server.lastPid is None:
+            servout += server.monitor()
+        if l4Type == 'TCP':
+            while 'Connected' not in client.cmd(
+                    'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
+                output('waiting for iperf to start up...')
+                sleep(.5)
+        cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
+                             bwArgs )
+        debug( 'Client output: %s\n' % cliout )
+        server.sendInt()
+        servout += server.waitOutput()
+        debug( 'Server output: %s\n' % servout )
+        result = [ self._parseIperf( servout ), self._parseIperf( cliout ) ]
+        if l4Type == 'UDP':
+            result.insert( 0, udpBw )
+        output( '*** Results: %s\n' % result )
+        return result
+
+    def runCpuLimitTest( self, cpu, duration=5 ):
+        """run CPU limit test with 'while true' processes.
+        cpu: desired CPU fraction of each host
+        duration: test duration in seconds
+        returns a single list of measured CPU fractions as floats.
+        """
+        pct = cpu * 100
+        info('*** Testing CPU %.0f%% bandwidth limit\n' % pct)
+        hosts = self.hosts
+        for h in hosts:
+            h.cmd( 'while true; do a=1; done &' )
+        pids = [h.cmd( 'echo $!' ).strip() for h in hosts]
+        pids_str = ",".join(["%s" % pid for pid in pids])
+        cmd = 'ps -p %s -o pid,%%cpu,args' % pids_str
+        # It's a shame that this is what pylint prefers
+        outputs = []
+        for _ in range( duration ):
+            sleep( 1 )
+            outputs.append( quietRun( cmd ).strip() )
+        for h in hosts:
+            h.cmd( 'kill %1' )
+        cpu_fractions = []
+        for test_output in outputs:
+            # Split by line.  Ignore first line, which looks like this:
+            # PID %CPU COMMAND\n
+            for line in test_output.split('\n')[1:]:
+                r = r'\d+\s*(\d+\.\d+)'
+                m = re.search( r, line )
+                if m is None:
+                    error( '*** Error: could not extract CPU fraction: %s\n' %
+                           line )
+                    return None
+                cpu_fractions.append( float( m.group( 1 ) ) )
+        output( '*** Results: %s\n' % cpu_fractions )
+        return cpu_fractions
+
+    # BL: I think this can be rewritten now that we have
+    # a real link class.
+    def configLinkStatus( self, src, dst, status ):
+        """Change status of src <-> dst links.
+           src: node name
+           dst: node name
+           status: string {up, down}"""
+        if src not in self.nameToNode:
+            error( 'src not in network: %s\n' % src )
+        elif dst not in self.nameToNode:
+            error( 'dst not in network: %s\n' % dst )
+        else:
+            if type( src ) is str:
+                src = self.nameToNode[ src ]
+            if type( dst ) is str:
+                dst = self.nameToNode[ dst ]
+            connections = src.connectionsTo( dst )
+            if len( connections ) == 0:
+                error( 'src and dst not connected: %s %s\n' % ( src, dst) )
+            for srcIntf, dstIntf in connections:
+                result = srcIntf.ifconfig( status )
+                if result:
+                    error( 'link src status change failed: %s\n' % result )
+                result = dstIntf.ifconfig( status )
+                if result:
+                    error( 'link dst status change failed: %s\n' % result )
+
+    def interact( self ):
+        "Start network and run our simple CLI."
+        self.start()
+        result = CLI( self )
+        self.stop()
+        return result
+
+    inited = False
+
+    @classmethod
+    def init( cls ):
+        "Initialize Mininet"
+        if cls.inited:
+            return
+        ensureRoot()
+        fixLimits()
+        cls.inited = True
+
+
+class MininetWithControlNet( Mininet ):
+
+    """Control network support:
+
+       Create an explicit control network. Currently this is only
+       used/usable with the user datapath.
+
+       Notes:
+
+       1. If the controller and switches are in the same (e.g. root)
+          namespace, they can just use the loopback connection.
+
+       2. If we can get unix domain sockets to work, we can use them
+          instead of an explicit control network.
+
+       3. Instead of routing, we could bridge or use 'in-band' control.
+
+       4. Even if we dispense with this in general, it could still be
+          useful for people who wish to simulate a separate control
+          network (since real networks may need one!)
+
+       5. Basically nobody ever used this code, so it has been moved
+          into its own class.
+
+       6. Ultimately we may wish to extend this to allow us to create a
+          control network which every node's control interface is
+          attached to."""
+
+    def configureControlNetwork( self ):
+        "Configure control network."
+        self.configureRoutedControlNetwork()
+
+    # We still need to figure out the right way to pass
+    # in the control network location.
+
+    def configureRoutedControlNetwork( self, ip='192.168.123.1',
+                                       prefixLen=16 ):
+        """Configure a routed control network on controller and switches.
+           For use with the user datapath only right now."""
+        controller = self.controllers[ 0 ]
+        info( controller.name + ' <->' )
+        cip = ip
+        snum = ipParse( ip )
+        for switch in self.switches:
+            info( ' ' + switch.name )
+            link = self.link( switch, controller, port1=0 )
+            sintf, cintf = link.intf1, link.intf2
+            switch.controlIntf = sintf
+            snum += 1
+            while snum & 0xff in [ 0, 255 ]:
+                snum += 1
+            sip = ipStr( snum )
+            cintf.setIP( cip, prefixLen )
+            sintf.setIP( sip, prefixLen )
+            controller.setHostRoute( sip, cintf )
+            switch.setHostRoute( cip, sintf )
+        info( '\n' )
+        info( '*** Testing control network\n' )
+        while not cintf.isUp():
+            info( '*** Waiting for', cintf, 'to come up\n' )
+            sleep( 1 )
+        for switch in self.switches:
+            while not sintf.isUp():
+                info( '*** Waiting for', sintf, 'to come up\n' )
+                sleep( 1 )
+            if self.ping( hosts=[ switch, controller ] ) != 0:
+                error( '*** Error: control network test failed\n' )
+                exit( 1 )
+        info( '\n' )