Added controller, dataplane and configuration files
diff --git a/src/python/oftest/controller/controller.py b/src/python/oftest/controller/controller.py
new file mode 100644
index 0000000..1526cee
--- /dev/null
+++ b/src/python/oftest/controller/controller.py
@@ -0,0 +1,113 @@
+"""
+OpenFlow Test Framework
+
+Controller class
+
+Provide the interface to the control channel to the switch under test.  
+
+Class inherits from thread so as to run in background allowing
+asynchronous callbacks (if needed, not required).  Also supports
+polling.
+
+The controller thread maintains a queue.  Incoming messages that
+are not handled by a callback function are placed in this queue for 
+poll calls.  
+
+Callbacks and polling support specifying the message type
+
+@todo Support transaction semantics via xid
+"""
+
+import sys
+sys.path.append("../ofmsg")
+import os
+import socket
+import time
+import promisc
+from threading import Thread
+from threading import Lock
+from message import *
+
+class Controller(Thread):
+    """
+    Class abstracting the control interface to the switch.  
+
+    For receiving messages, two mechanism will be implemented.  First,
+    query the interface with poll.  Second, register to have a
+    function called by message type.  The callback is passed the
+    message type as well as the raw packet (or message object)
+
+    One of the main purposes of this object is to translate between network 
+    and host byte order.  'Above' this object, things should be in host
+    byte order.
+    """
+
+    def __init__(port=6633, passive=1):
+        if (passive):
+            # FIXME: add error handling
+            self.sock = open_ctrlsocket()
+            self.clientsock, self.clientaddr = self.sock.accept()
+        else:
+            print "Error in controller init: Active cxn not supported"
+
+    def register(self, msg_type, handler):
+        """
+        Register a callback to receive a specific message type.
+
+        Only one handler may be registered for a given message type.
+        @param msg_type The type of message to receive.  May be DEFAULT 
+        for all non-handled packets.  
+        @param handler The function to call when a message of the given 
+        type is received.
+        """
+        print "Controller message handler registration not supported"
+
+    def poll(self, exp_msg=None, timeout=None):
+        """
+        Wait for the next OF message received from the switch.
+
+        @param exp_msg If set, return only when this type of message 
+        is received.
+
+        @param timeout If set, return E_TIMEOUT if mesage is not
+        received in this time.  If set to 0, will not block.
+
+        @retval A triple (msg_type, msg, data) where msg_type is the OpenFlow 
+        message type value OFPT_xxx, msg is a message object (from a 
+        SWIG generated class) appropriate to the message type and data is
+        a string of any additional information following the 
+        normal message.  Note that
+        many messages do not have classes so ofp_hello is returned which 
+        simply has the header.
+        The data members in the message are in host endian order.
+        If a timeout (or other error) occurs, None is returned
+        """
+        while 1:
+            okay, pkt = rcv_data_from_socket(self.clientsoc, timeout)
+            if not okay or not pkt:
+                # FIXME: Check for error
+                return None, None
+            # Convert msg to the proper OpenFlow message object
+            msg_type, msg = ofpkt.pkt_to_msg(pkt)
+            print "DEBUG: Got msg type %d of len %d" % (msg_type, len(msg))
+
+            if not exp_msg or (exp_msg and (hdr.type == exp_msg)):
+                return msg_type, msg
+
+    def flow_install(self, flow):
+        """
+        Install the flow indicated through the control interface
+        TBD:  We may just use message_send below with ofp_flow_mod objects
+        @param flow The ofp_flow_mod object to install
+        """
+
+    def message_send(self, msg):
+        """
+        Send the message to the switch
+        @param msg An OpenFlow message object (from a SWIG generated
+        class) to be forwarded to the switch.  The data members of the
+        object must be in host endian order when pased to message_send.
+        """
+
+    def kill(self):
+        self.clientsock.close()
diff --git a/src/python/oftest/dataplane/dataplane.py b/src/python/oftest/dataplane/dataplane.py
new file mode 100644
index 0000000..7a2e8cd
--- /dev/null
+++ b/src/python/oftest/dataplane/dataplane.py
@@ -0,0 +1,98 @@
+"""
+OpenFlow Test Framework
+
+Dataplane class
+
+Provide the interface to the control the set of ports being used
+to stimulate the switch under test.
+
+See the class dataplaneport for more details.  This class wraps
+a set of those objects allowing general calls and parsing 
+configuration.
+
+"""
+
+from dataplaneport import *
+
+class DataPlane:
+    """
+    Class defining access primitives to the data plane
+    Controls a list of DataPlanePort objects
+    """
+    def __init__(self):
+        self.port_list = {}
+
+    def port_add(self, interface_name, port_number):
+        """
+        Add a port to the dataplane
+        TBD:  Max packets for queue?
+        @param interface_name The name of the physical interface like eth1
+        @param port_number The port number used to refer to the port
+        """
+        
+        self.port_list[port_number] = DataPlanePort(interface_name)
+        self.port_list[port_number].start()
+
+    def send(self, port_number, packet):
+        """
+        Send a packet to the given port
+        @param port_number The port to send the data to
+        @param packet Raw packet data to send to port
+        """
+        bytes = self.port_list[port_number].send(packet)
+        if bytes != len(packet):
+            print "Unhandled send error, length mismatch %d != %d" % \
+                (bytes, len(packet))
+        return bytes
+
+    def flood(self, packet):
+        """
+        Send a packet to all ports
+        @param packet Raw packet data to send to port
+        """
+        for port_number in self.port_list.keys():
+            bytes = self.port_list[port_number].send(packet)
+            if bytes != len(packet):
+                print "Unhandled send error" + \
+                    ", port %d, length mismatch %d != %d" % \
+                    (port_number, bytes, len(packet))
+
+    def packet_get(self, port_number=None):
+        """
+        Get a packet from the data plane
+        If port_number is given, get the packet from that port.
+        Otherwise, find the port with the oldest packet and return
+        that packet.
+        @param port_number If set, get packet from this port
+        @retval The triple port_number, packet, pkt_time where packet 
+        is received from port_number at time pkt_time.  
+        """
+
+        if port_number:
+            if self.port_list[port_number].packets_pending != 0:
+                pkt, time = self.port_list[port_number].dequeue()
+                return port_number, pkt, time
+            else:
+                return None, None, None
+
+        # Find port with oldest packet
+        min_time = 0
+        min_port = -1
+        for port_number in self.port_list.keys():
+            ptime = self.port_list[port_number].timestamp_head()
+            if ptime:
+                if (min_port == -1) or (ptime < min_time):
+                    min_time = ptime 
+                    min_port = port_number
+
+        if min_port == -1:
+            return None, None, None
+
+        pkt, time = self.port_list[min_port].dequeue()
+        return min_port, pkt, time
+
+    def kill(self):
+        for port_number in self.port_list.keys():
+            self.port_list[port_number].kill()
+
+        print "DataPlane shutdown"
diff --git a/src/python/oftest/dataplane/dataplaneport.py b/src/python/oftest/dataplane/dataplaneport.py
new file mode 100644
index 0000000..f461dfa
--- /dev/null
+++ b/src/python/oftest/dataplane/dataplaneport.py
@@ -0,0 +1,152 @@
+"""
+OpenFlow Test Framework
+
+Dataplane Port class
+
+Control a dataplane port connected to the switch under test.
+
+Class inherits from thread so as to run in background allowing
+asynchronous callbacks (if needed, not required).  Also supports
+polling.
+
+The port object thread maintains a queue.  Incoming packets that
+are not handled by a callback function are placed in this queue for 
+poll calls.  
+
+"""
+
+import sys
+sys.path.append("../packet")  # Needed?
+import os
+import socket
+import time
+import promisc
+from threading import Thread
+from threading import Lock
+
+ETH_P_ALL = 0x03
+RCV_TIMEOUT = 10000
+RCV_SIZE = 4096
+
+class DataPlanePort(Thread):
+    """
+    Class defining a port monitoring object.
+    Creates a promiscuous socket on a physical interface
+    Queues the packets received on that interface with time stamps
+    Inherits from Thread class as meant to run in background
+    Use accessors to dequeue packets for proper synchronization
+    """
+
+    def __init__(self, interface_name, max_pkts=1024):
+        """
+        Set up a port monitor object
+        @param interface_name The name of the physical interface like eth1
+        @param max_pkts Maximum number of pkts to keep in queue
+        """
+        Thread.__init__(self)
+        self.interface_name = interface_name
+        self.max_pkts = max_pkts
+        self.packets_pending = 0
+        self.packets_total = 0
+        self.packets = []
+        self.packet_times = []
+        self.sync = Lock()
+        self.socket = self.interface_open(interface_name)
+        print "Openned port monitor socket " + interface_name
+
+    def interface_open(self, interface_name):
+        """
+        Open a socket in a promiscuous mode for a data connection.
+        @param interface_name port name as a string such as 'eth1'
+        @retval s socket
+        """
+        s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, 
+                          socket.htons(ETH_P_ALL))
+        s.bind((interface_name, 0))
+        promisc.set_promisc(s, interface_name)
+        s.settimeout(RCV_TIMEOUT)
+        return s
+
+    def kill(self):
+        """
+        Terminate the running thread
+        """
+        self.running = False
+        self.socket.close()
+        print "Port monitor for " + self.interface_name + " exiting"
+
+    def run(self):
+        """
+        Activity function for class
+        """
+        self.running = True
+        while self.running:
+            try:
+                rcvmsg = self.socket.recv(RCV_SIZE)
+                rcvtime = time.clock()
+
+                self.sync.acquire()
+                self.packets.append(rcvmsg)
+                self.packet_times.append(rcvtime)
+                self.packets_pending += 1
+                self.packets_total += 1
+                self.sync.release()
+
+            except socket.timeout:
+                print "Socket timeout for " + self.interface_name
+            except socket.error:
+                print "Socket closed for " + self.interface_name
+                if self.running:
+                    self.kill()
+                break
+
+    def dequeue(self):
+        """
+        Get the oldest packet in the queue
+        """
+        self.sync.acquire()
+        pkt = self.packets.pop(0)
+        pkt_time = self.packet_times.pop(0)
+        self.packets_pending -= 1
+        self.sync.release()
+        return pkt, pkt_time
+
+    def timestamp_head(self):
+        """
+        Return the timestamp of the head of queue or None if empty
+        """
+        if self.packets_pending:
+            return self.packet_times[0]
+        return None
+
+    def flush(self):
+        """
+        Clear the packet queue
+        """
+        self.sync.acquire()
+        self.packets = []
+        self.packet_times = []
+        self.packets_pending = 0
+        self.sync.release()
+
+
+    def send(self, packet):
+        """
+        Send a packet to the dataplane port
+        @param packet The packet data to send to the port
+        @retval The number of bytes sent
+        """
+        return self.socket.send(packet)
+
+
+    def register(self, handler):
+        """
+        Register a callback function to receive packets from this
+        port.  The callback will be passed the packet, the 
+        interface name and the port number (if set) on which the 
+        packet was received.
+
+        To be implemented
+        """
+        pass
+
diff --git a/src/python/oftest/oft_config.py b/src/python/oftest/oft_config.py
new file mode 100644
index 0000000..643c63a
--- /dev/null
+++ b/src/python/oftest/oft_config.py
@@ -0,0 +1,97 @@
+"""
+OpenFlow Test Framework
+
+Configuration fragment
+
+This file contains Python code to specify the configuration
+of the system under test.
+
+This is a work in progress.  The code below is for illustration only.
+
+The configuration information is extensible in that any 
+Python code may be added to this file (or its imports) and will 
+be available to test cases.  
+
+A platform identifier is given at the top of the file and most
+other information is determined by testing this value.  Additional
+files may also be imported based on the platform.
+
+The configuration must specify a mapping from system interfaces
+available to the test framework to OpenFlow port numbers on the
+switch under test.  This is done in the interface_ofport_map
+dictionary.  Future extensions may include specifying a driver
+for the port (so as to allow remote packet generation) and to
+specify a switch instance (to allow multiple switches to be
+tested together).
+
+Currently, the assumption is that ports are bidirectional, so
+specifying "OF port 1 is connnected to eth2" implies this is so
+for both TX and RX.
+
+"""
+
+##@var platform
+# A string representing the platform under test.  Tested below
+# for determining other variables.
+
+##@var switch_cxn_type
+# How does the test framework communicate to the switch?
+#
+# Options include:
+# @arg localhost:   Switch is running on same host as tests
+# @arg ssh:         Use paramiko to control ssh connection. Needs switch
+# IP and other params
+#
+# For ssh, additional variables include
+# @arg switch_ip = "192.168.2.21"
+# @arg switch_username = "root"
+# @arg switch_password
+
+##@var switch_init
+# A function to be called to initialize the switch.  May be None
+# to indicate no such function needs to be called.
+
+##@var switch_connect
+# Function to be called to prompt the switch to connect to the
+# controller.  The function may need to maintain connection state 
+# as it could be called multiple times between disconnects.
+
+##@var switch_disconnect
+# Function to be called to force the switch to disconnect from the
+# controller. 
+
+##@var controller_ip
+# Gives the controller IP address to use
+
+##@var controller_port
+# Gives the controller port to use
+
+platform = "sw_userspace"
+# platform = "sw_kernelspace"
+# platform = "bcm_indigo"
+# platform = "stanford_lb4g"
+
+if platform == "sw_userspace":
+    interface_ofport_map = {
+        1 : "eth2",
+        2 : "eth3",
+        3 : "eth4",
+        4 : "eth5"
+        }
+    switch_cxn_type = "localhost"
+    switch_init = None  # TBD
+    switch_connect = None  # TBD
+    switch_disconnect = None  # TBD
+    controller_ip = "172.27.74.158"
+    controller_port = 7000
+
+elif platform == "bcm_indigo":
+    switch_cxn_type = "ssh"
+    switch_ip = "192.168.2.21"
+    switch_username = "root"
+    switch_password = "OpenFlow"
+    switch_init = None  # TBD
+    switch_connect = None  # TBD
+    switch_disconnect = None  # TBD
+    controller_ip = "172.27.74.158"
+    controller_port = 7000