diff --git a/bin/mn_run.py b/bin/mn_run.py
index e0fb1604748cb3450b70a3bcdbf44b40a6ef9b57..fc65bacb3132e0b16f472a5037539fc3a85579e9 100755
--- a/bin/mn_run.py
+++ b/bin/mn_run.py
@@ -7,25 +7,33 @@
 from optparse import OptionParser
 import time
 
-from ripcord.topo import FatTreeTopo, SingleSwitchTopo, LinearTopo
+try:
+    from ripcord.dctopo import TreeTopo, FatTreeTopo, VL2Topo
+    USE_RIPCORD = True
+except ImportError:
+    USE_RIPCORD = False
 
 from mininet.logging_mod import lg, set_loglevel, LEVELS
 from mininet.net import Mininet, init
 from mininet.node import KernelSwitch, Host, Controller, ControllerParams, NOX
-from mininet.topo import TreeTopo
+from mininet.topo import SingleSwitchTopo, LinearTopo
 
 # built in topologies, created only when run
 TOPO_DEF = 'minimal'
-TOPOS = {'minimal' :  (lambda: SingleSwitchTopo(k = 2)),
+TOPOS = {'minimal' :   (lambda: SingleSwitchTopo(k = 2)),
+         'single4' :   (lambda: SingleSwitchTopo(k = 4)),
+         'single100' : (lambda: SingleSwitchTopo(k = 100)),
+         'linear2' :   (lambda: LinearTopo(k = 2)),
+         'linear100' : (lambda: LinearTopo(k = 100))}
+if USE_RIPCORD:
+    TOPOS_RIPCORD = {
          'tree16' :   (lambda: TreeTopo(depth = 3, fanout = 4)),
          'tree64' :   (lambda: TreeTopo(depth = 4, fanout = 4)),
          'tree1024' : (lambda: TreeTopo(depth = 3, fanout = 32)),
          'fattree4' : (lambda: FatTreeTopo(k = 4)),
          'fattree6' : (lambda: FatTreeTopo(k = 6)),
-         'single4' :  (lambda: SingleSwitchTopo(k = 4)),
-         'single100' : (lambda: SingleSwitchTopo(k = 100)),
-         'linear2' :  (lambda: LinearTopo(k = 2)),
-         'linear100' : (lambda: LinearTopo(k = 100))}
+         'vl2'      : (lambda: VL2Topo(da = 4, di = 4))}
+    TOPOS.update(TOPOS_RIPCORD)
 
 SWITCH_DEF = 'kernel'
 SWITCHES = {'kernel' : KernelSwitch}
@@ -106,7 +114,8 @@ def setup(self):
         init()
 
         # check for invalid combinations
-        if 'fattree' in self.options.topo and self.options.controller == 'ref':
+        if self.options.controller == 'ref' and \
+            (('fattree' in self.options.topo) or ('vl2' in self.options.topo)):
             raise Exception('multipath topos require multipath-capable '
                             'controller.')
 
diff --git a/mininet/test/test_nets.py b/mininet/test/test_nets.py
index 02b61d6b375c6aa1ba236a03f9cdf23435b700b4..88296869de0c5b31ba81b1e0189abdf56c032634 100755
--- a/mininet/test/test_nets.py
+++ b/mininet/test/test_nets.py
@@ -7,16 +7,14 @@
 from time import sleep
 import unittest
 
-from ripcord.topo import SingleSwitchTopo, LinearTopo
-
 from mininet.net import init, Mininet
-from mininet.node import KernelSwitch, Host, ControllerParams
-from mininet.node import Controller
-from mininet.topo import TreeTopo
+from mininet.node import KernelSwitch, Host, Controller, ControllerParams
+from mininet.topo import SingleSwitchTopo, LinearTopo
 
 # temporary, until user-space side is tested
 SWITCHES = {'kernel' : KernelSwitch}
 
+
 class testSingleSwitch(unittest.TestCase):
     '''For each datapath type, test ping with single switch topologies.'''
 
@@ -55,20 +53,5 @@ def testLinear5(self):
             self.assertEqual(dropped, 0)
 
 
-class testTree(unittest.TestCase):
-    '''For each datapath type, test all-pairs ping with TreeNet.'''
-
-    def testTree9(self):
-        '''Ping test with both datapaths on 9-host topology'''
-        init()
-        for switch in SWITCHES.values():
-            controller_params = ControllerParams(0x0a000000, 8) # 10.0.0.0/8
-            tree_topo = TreeTopo(depth = 3, fanout = 3)
-            mn = Mininet(tree_topo, switch, Host, Controller,
-                         controller_params)
-            dropped = mn.run('ping')
-            self.assertEqual(dropped, 0)
-
-
 if __name__ == '__main__':
     unittest.main()
\ No newline at end of file
diff --git a/mininet/topo.py b/mininet/topo.py
index 702212dda8c5d83a1a8e1987bbac7d0a8833133a..1f2a963540bc160294cf238f9603665ad576d9e6 100644
--- a/mininet/topo.py
+++ b/mininet/topo.py
@@ -1,96 +1,259 @@
 #!/usr/bin/env python
-'''Starter topologies for Mininet.'''
+'''@package topo
 
-from ripcord.topo import Topo, StructuredNodeSpec, StructuredNode, Edge
-from ripcord.topo import StructuredTopo, StructuredEdgeSpec, NodeID
+Network topology creation.
 
-class TreeTopo(StructuredTopo):
-    '''Tree-structured network.'''
+@author Brandon Heller (brandonh@stanford.edu)
 
-    class TreeNodeID(NodeID):
-        '''Tree-specific node.'''
+This package includes code to represent network topologies.
 
-        def __init__(self, layer = 0, index = 0, dpid = None):
-            '''Create TreeNodeID object from custom params.
+A Topo object can be a topology database for NOX, can represent a physical
+setup for testing, and can even be emulated with the Mininet package.
+'''
 
-            Either (layer, index) or dpid must be passed in.
+from networkx import Graph
 
-            @param layer layer
-            @param index index within layer
-            @param dpid optional dpid
-            '''
-            if dpid:
-                self.layer = (dpid & 0xff0000) >> 16
-                self.index = (dpid & 0xffff)
-                self.dpid = dpid
-            else:
-                self.layer = layer
-                self.index = index
-                self.dpid = (layer << 16) + index
 
-        def __str__(self):
-            return "(%i_%i)" % (self.layer, self.index)
+class NodeID(object):
+    '''Topo node identifier.'''
 
-        def name_str(self):
-            return "%i_%i" % (self.layer, self.index)
+    def __init__(self, dpid = None):
+        '''Init.
+
+        @param dpid dpid
+        '''
+        # DPID-compatible hashable identifier: opaque 64-bit unsigned int
+        self.dpid = dpid
+
+    def __str__(self):
+        '''String conversion.
+
+        @return str dpid as string
+        '''
+        return str(self.dpid)
+
+    def name_str(self):
+        '''Name conversion.
 
-        def ip_str(self):
-            # add 1; can't have IP addr ending in 0
-            index_hi = (self.index & 0xff00) >> 8
-            index_lo = self.index & 0xff
-            return "10.%i.%i.%i" % (self.layer, index_hi, index_lo)
+        @return name name as string
+        '''
+        return str(self.dpid)
+
+    def ip_str(self):
+        '''Name conversion.
+
+        @return ip ip as string
+        '''
+        hi = (self.dpid & 0xff0000) >> 16
+        mid = (self.dpid & 0xff00) >> 8
+        lo = self.dpid & 0xff
+        return "10.%i.%i.%i" % (hi, mid, lo)
 
-    def __init__(self, depth = 2, fanout = 2, speed = 1.0, enable_all = True):
+
+class Node(object):
+    '''Node-specific vertex metadata for a Topo object.'''
+
+    def __init__(self, connected = False, admin_on = True,
+                 power_on = True, fault = False, is_switch = True):
         '''Init.
 
-        @param depth number of levels, including host level
-        @param fanout
-        '''
-        node_specs = []
-        core = StructuredNodeSpec(0, fanout, None, speed, type_str = 'core')
-        node_specs.append(core)
-        for i in range(1, depth - 1):
-            node = StructuredNodeSpec(1, fanout, speed, speed,
-                                      type_str = 'layer' + str(i))
-            node_specs.append(node)
-        host = StructuredNodeSpec(1, 0, speed, None, type_str = 'host')
-        node_specs.append(host)
-        edge_specs = [StructuredEdgeSpec(speed)] * (depth - 1)
-        super(TreeTopo, self).__init__(node_specs, edge_specs)
-
-        self.depth = depth
-        self.fanout = fanout
-        self.id_gen = TreeTopo.TreeNodeID
-
-        # create root
-        root_id = self.id_gen(0, 0).dpid
-        self._add_node(root_id, StructuredNode(0))
-        last_layer_ids = [root_id]
-
-        # create lower layers
-        for i in range(1, depth):
-            current_layer_ids = []
-            # start index at 1, as we can't have IP addresses ending in 0
-            index = 1
-            for last_id in last_layer_ids:
-                for j in range(fanout):
-                    is_switch = (i < depth - 1)
-                    node = StructuredNode(i, is_switch = is_switch)
-                    node_id = self.id_gen(i, index).dpid
-                    current_layer_ids.append(node_id)
-                    self._add_node(node_id, node)
-                    self._add_edge(last_id, node_id, Edge())
-                    index += 1
-            last_layer_ids = current_layer_ids
+        @param connected actively connected to controller
+        @param admin_on administratively on or off
+        @param power_on powered on or off
+        @param fault fault seen on node
+        @param is_switch switch or host
+        '''
+        self.connected = connected
+        self.admin_on = admin_on
+        self.power_on = power_on
+        self.fault = fault
+        self.is_switch = is_switch
 
-        if enable_all:
-            self.enable_all()
 
-    def port(self, src, dst):
-        '''Get port number
+class Edge(object):
+    '''Edge-specific metadata for a StructuredTopo graph.'''
+
+    def __init__(self, admin_on = True, power_on = True, fault = False):
+        '''Init.
+
+        @param admin_on administratively on or off; defaults to True
+        @param power_on powered on or off; defaults to True
+        @param fault fault seen on edge; defaults to False
+        '''
+        self.admin_on = admin_on
+        self.power_on = power_on
+        self.fault = fault
+
+
+class Topo(object):
+    '''Data center network representation for structured multi-trees.'''
+    def __init__(self):
+        '''Create Topo object.
+
+        '''
+        self.g = Graph()
+        self.node_info = {} # dpids hash to Node objects
+        self.edge_info = {} # (src_dpid, dst_dpid) tuples hash to Edge objects
+        self.ports = {} # ports[src][dst] is port on src that connects to dst
+        self.id_gen = NodeID # class used to generate dpid
+
+    def _add_node(self, dpid, node):
+        '''Add Node to graph.
+
+        @param dpid dpid
+        @param node Node object
+        '''
+        self.g.add_node(dpid)
+        self.node_info[dpid] = node
+
+    def _add_edge(self, src, dst, edge):
+        '''Add edge (Node, Node) to graph.
+
+        @param src src dpid
+        @param dst dst dpid
+        @param edge Edge object
+        '''
+        src, dst = tuple(sorted([src, dst]))
+        self.g.add_edge(src, dst)
+        self.edge_info[(src, dst)] = edge
+        self._add_port(src, dst)
 
-        Note that the topological significance of DPIDs enables
-        this function to be implemented statelessly.
+    def _add_port(self, src, dst):
+        '''Generate port mapping for new edge.
+
+        @param src source switch DPID
+        @param dst destination switch DPID
+        '''
+        if src not in self.ports:
+            self.ports[src] = {}
+        if dst not in self.ports[src]:
+            self.ports[src][dst] = len(self.ports[src]) # num outlinks
+
+        if dst not in self.ports:
+            self.ports[dst] = {}
+        if src not in self.ports[dst]:
+            self.ports[dst][src] = len(self.ports[dst]) # num outlinks
+
+    def node_enabled(self, dpid):
+        '''Is node connected, admin on, powered on, and fault-free?
+
+        @param dpid dpid
+
+        @return bool node is enabled
+        '''
+        ni = self.node_info[dpid]
+        return ni.connected and ni.admin_on and ni.power_on and not ni.fault
+
+    def nodes_enabled(self, dpids, enabled = True):
+        '''Return subset of enabled nodes
+
+        @param dpids list of dpids
+        @param enabled only return enabled nodes?
+
+        @return dpids filtered list of dpids
+        '''
+        if enabled:
+            return [n for n in dpids if self.node_enabled(n)]
+        else:
+            return dpids
+
+    def nodes(self, enabled = True):
+        '''Return graph nodes.
+
+        @param enabled only return enabled nodes?
+
+        @return dpids list of dpids
+        '''
+        return self.nodes_enabled(self.g.nodes(), enabled)
+
+    def nodes_str(self, dpids):
+        '''Return string of custom-encoded nodes.
+
+        @param dpids list of dpids
+
+        @return str string
+        '''
+        return [str(self.id_gen(dpid = dpid)) for dpid in dpids]
+
+    def switches(self, enabled = True):
+        '''Return switches.
+
+        @param enabled only return enabled nodes?
+
+        @return dpids list of dpids
+        '''
+        def is_switch(n):
+            '''Returns true if node is a switch.'''
+            return self.node_info[n].is_switch
+
+        nodes = [n for n in self.g.nodes() if is_switch(n)]
+        return self.nodes_enabled(nodes, enabled)
+
+    def hosts(self, enabled = True):
+        '''Return hosts.
+
+        @param enabled only return enabled nodes?
+
+        @return dpids list of dpids
+        '''
+        def is_host(n):
+            '''Returns true if node is a host.'''
+            return not self.node_info[n].is_switch
+
+        nodes = [n for n in self.g.nodes() if is_host(n)]
+        return self.nodes_enabled(nodes, enabled)
+
+    def edge_enabled(self, edge):
+        '''Is edge admin on, powered on, and fault-free?
+
+        @param edge (src, dst) dpid tuple
+
+        @return bool edge is enabled
+        '''
+        src, dst = edge
+        src, dst = tuple(sorted([src, dst]))
+        ei = self.edge_info[tuple(sorted([src, dst]))]
+        return ei.admin_on and ei.power_on and not ei.fault
+
+    def edges_enabled(self, edges, enabled = True):
+        '''Return subset of enabled edges
+
+        @param edges list of edges
+        @param enabled only return enabled edges?
+
+        @return edges filtered list of edges
+        '''
+        if enabled:
+            return [e for e in edges if self.edge_enabled(e)]
+        else:
+            return edges
+
+    def edges(self, enabled = True):
+        '''Return edges.
+
+        @param enabled only return enabled edges?
+
+        @return edges list of dpid pairs
+        '''
+        return self.edges_enabled(self.g.edges(), enabled)
+
+    def edges_str(self, dpid_pairs):
+        '''Return string of custom-encoded node pairs.
+
+        @param dpid_pairs list of dpid pairs (src, dst)
+
+        @return str string
+        '''
+        edges = []
+        for pair in dpid_pairs:
+            src, dst = pair
+            src = str(self.id_gen(dpid = src))
+            dst = str(self.id_gen(dpid = dst))
+            edges.append((src, dst))
+        return edges
+
+    def port(self, src, dst):
+        '''Get port number.
 
         @param src source switch DPID
         @param dst destination switch DPID
@@ -98,25 +261,38 @@ def port(self, src, dst):
             src_port: port on source switch leading to the destination switch
             dst_port: port on destination switch leading to the source switch
         '''
+        if src in self.ports and dst in self.ports[src]:
+            assert dst in self.ports and src in self.ports[dst]
+            return (self.ports[src][dst], self.ports[dst][src])
 
-        src_layer = self.node_info[src].layer
-        dst_layer = self.node_info[dst].layer
+    def enable_edges(self):
+        '''Enable all edges in the network graph.
 
-        src_id = self.id_gen(dpid = src)
-        dst_id = self.id_gen(dpid = dst)
+        Set admin on, power on, and fault off.
+        '''
+        for e in self.g.edges():
+            src, dst = e
+            ei = self.edge_info[tuple(sorted([src, dst]))]
+            ei.admin_on = True
+            ei.power_on = True
+            ei.fault = False
 
-        lower = None
-        higher = None
-        if src_layer == dst_layer - 1: # src is higher
-            src_port = ((dst_id.index - 1) % self.fanout) + 1
-            dst_port = 0
-        elif dst_layer == src_layer - 1:
-            src_port = 0
-            dst_port = ((src_id.index - 1) % self.fanout) + 1
-        else:
-            raise Exception("Could not find port leading to given dst switch")
+    def enable_nodes(self):
+        '''Enable all nodes in the network graph.
 
-        return (src_port, dst_port)
+        Set connected on, admin on, power on, and fault off.
+        '''
+        for node in self.g.nodes():
+            ni = self.node_info[node]
+            ni.connected = True
+            ni.admin_on = True
+            ni.power_on = True
+            ni.fault = False
+
+    def enable_all(self):
+        '''Enable all nodes and edges in the network graph.'''
+        self.enable_nodes()
+        self.enable_edges()
 
     def name(self, dpid):
         '''Get string name of node ID.
@@ -132,4 +308,54 @@ def ip(self, dpid):
         @param dpid DPID of host or switch
         @return ip_str
         '''
-        return self.id_gen(dpid = dpid).ip_str()
\ No newline at end of file
+        return self.id_gen(dpid = dpid).ip_str()
+
+
+class SingleSwitchTopo(Topo):
+    '''Single switch connected to k hosts.'''
+
+    def __init__(self, k = 2, enable_all = True):
+        '''Init.
+
+        @param k number of hosts
+        @param enable_all enables all nodes and switches?
+        '''
+        super(SingleSwitchTopo, self).__init__()
+
+        self.k = k
+
+        self._add_node(0, Node())
+        hosts = range(1, k + 1)
+        for h in hosts:
+            self._add_node(h, Node(is_switch = False))
+            self._add_edge(h, 0, Edge())
+
+        if enable_all:
+            self.enable_all()
+
+
+class LinearTopo(Topo):
+    '''Linear topology of k switches, with one host per switch.'''
+
+    def __init__(self, k = 2, enable_all = True):
+        '''Init.
+
+        @param k number of switches (and hosts too)
+        @param enable_all enables all nodes and switches?
+        '''
+        super(LinearTopo, self).__init__()
+
+        self.k = k
+
+        switches = range(0, k)
+        for s in switches:
+            h = s + k
+            self._add_node(s, Node())
+            self._add_node(h, Node(is_switch = False))
+            self._add_edge(s, h, Edge())
+        for s in switches:
+            if s != k - 1:
+                self._add_edge(s, s + 1, Edge())
+
+        if enable_all:
+            self.enable_all()
\ No newline at end of file