Added controller, dataplane and configuration files
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
+