Initial oftest skeleton with wrapper generators and pylibopenflow
diff --git a/tools/pylibopenflow/pylib/of/__init__.py b/tools/pylibopenflow/pylib/of/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/pylibopenflow/pylib/of/__init__.py
diff --git a/tools/pylibopenflow/pylib/of/msg.py b/tools/pylibopenflow/pylib/of/msg.py
new file mode 100644
index 0000000..8617f56
--- /dev/null
+++ b/tools/pylibopenflow/pylib/of/msg.py
@@ -0,0 +1,117 @@
+"""This module parses OpenFlow packets.
+
+Unfortunately, this has to be updated manually for each OpenFlow version
+and packet type. Ugly.
+
+(C) Copyright Stanford University
+Date October 2009
+Created by ykk
+"""
+class parser:
+ """Parser for OpenFlow packets
+
+ (C) Copyright Stanford University
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self, messages):
+ """Initialize
+ """
+ ##Internal reference to OpenFlow messages
+ self.__messages = messages
+
+ def describe(self, packet):
+ """Parse OpenFlow packet and return string description
+ """
+ dic = self.__messages.peek_from_front("ofp_header", packet)
+ desc = self.header_describe(dic)
+ if (dic["type"][0] == self.__messages.get_value("OFPT_HELLO")):
+ pass
+ elif (dic["type"][0] == self.__messages.get_value("OFPT_SET_CONFIG")):
+ desc += "\n\t"+self.switch_config_describe(packet)
+ elif (dic["type"][0] == self.__messages.get_value("OFPT_FLOW_MOD")):
+ (fmdic, remaining) = self.__messages.unpack_from_front("ofp_flow_mod", packet)
+ desc += self.flow_mod_describe(fmdic, "\n\t")
+ desc += "\n\twith remaining "+str(len(remaining))+" bytes"
+ else:
+ desc += "\n\tUnparsed..."
+ return desc
+
+ def flow_mod_describe(self, packet, prefix=""):
+ """Parse flow mod and return description
+ """
+ dic = self.__assert_dic(packet, "ofp_flow_mod")
+ if (dic == None):
+ return ""
+ return prefix+\
+ "Flow_mod of command "+self.__messages.get_enum_name("ofp_flow_mod_command", dic["command"][0])+\
+ " idle/hard timeout:"+str(dic["idle_timeout"][0])+"/"+str(dic["hard_timeout"][0])+\
+ self.match_describe(dic, "match.", "\n\t")+\
+ prefix+\
+ "(priority:"+str(dic["priority"][0])+\
+ "/buffer id:"+str(dic["buffer_id"][0])+\
+ "/out port:"+str(dic["out_port"][0])+")"
+
+ def match_describe(self, dic, nameprefix="", prefix=""):
+ """Return description for ofp match
+ """
+ return prefix+"match wildcards:%x" % dic[nameprefix+"wildcards"][0]+\
+ " inport="+str(dic[nameprefix+"in_port"][0])+\
+ prefix+" "+\
+ " ethertype="+str(dic[nameprefix+"dl_type"][0])+\
+ " vlan="+str(dic[nameprefix+"dl_vlan"][0])+\
+ " "+self.eth_describe(dic[nameprefix+"dl_src"])+"->"+\
+ self.eth_describe(dic[nameprefix+"dl_dst"])+\
+ prefix+" "+\
+ " ipproto="+str(dic[nameprefix+"nw_proto"][0])+\
+ " "+self.ip_describe(dic[nameprefix+"nw_src"][0])+\
+ "->"+self.ip_describe(dic[nameprefix+"nw_src"][0])+\
+ prefix+" "+\
+ " transport "+str(dic[nameprefix+"tp_src"][0])+\
+ "->"+str(dic[nameprefix+"tp_dst"][0])
+
+ def switch_config_describe(self, packet):
+ """Parse OpenFlow switch config and return description
+ """
+ dic = self.__assert_dic(packet, "ofp_switch_config")
+ if (dic == None):
+ return ""
+ return "with flag "+str(self.__messages.get_enum_name("ofp_config_flags", dic["flags"][0]))+\
+ " and miss send length "+str(dic["miss_send_len"][0])
+
+ def header_describe(self, packet):
+ """Parse OpenFlow header and return string description
+ """
+ dic = self.__assert_dic(packet, "ofp_header")
+ if (dic == None):
+ return ""
+ return self.__messages.get_enum_name("ofp_type", dic["type"][0])+" packet "+\
+ "(length:"+str(dic["length"][0])+\
+ "/xid:"+str(dic["xid"][0])+")"
+
+ def ip_describe(self, value):
+ """Return string for ip address
+ """
+ desc = ""
+ for i in range(0,4):
+ (value, cv) = divmod(value, 256)
+ desc = str(cv).strip() +"." + desc
+ return desc
+
+ def eth_describe(self, etheraddr):
+ """Return string for ethernet address
+ """
+ desc = ""
+ for value in etheraddr:
+ desc += ":"+("%x" % value).zfill(2)
+ return desc[1:]
+
+ def __assert_dic(self, packet, typename):
+ """Assert and ensure dictionary is given
+ """
+ dic = None
+ if (isinstance(packet, str)):
+ dic = self.__messages.peek_from_front(typename, packet)
+ elif (isinstance(packet, dict)):
+ dic = packet
+ return dic
diff --git a/tools/pylibopenflow/pylib/of/network.py b/tools/pylibopenflow/pylib/of/network.py
new file mode 100644
index 0000000..6765a12
--- /dev/null
+++ b/tools/pylibopenflow/pylib/of/network.py
@@ -0,0 +1,191 @@
+"""This module holds the network.
+
+Copyright(C) 2009, Stanford University
+Date October 2009
+Created by ykk
+"""
+import random
+import openflow
+
+class network:
+ """Class holding information about OpenFlow network
+ """
+ def __init__(self):
+ """Initialize
+ """
+ ##List of switches
+ self.switches = []
+ ##Dictionary of links
+ self.links = {}
+ ##Reference to connections
+ self.connections = openflow.connections()
+
+ def add_switch(self, sw):
+ """Add switch to network
+ """
+ self.switches.append(sw)
+ self.connections.add_connection(sw, sw.connection)
+
+ def add_link(self, link):
+ """Add link to network
+ """
+ try:
+ self.links[link.switch1,link.switch2].append(link)
+ except KeyError:
+ self.links[link.switch1,link.switch2] = []
+ self.links[link.switch1,link.switch2].append(link)
+
+class link:
+ """Class to hold information about link
+
+ Copyright(C) 2009, Stanford University
+ Date November 2009
+ Created by ykk
+ """
+ def __init__(self, switch1, switch2):
+ """Initialize link between specified switches
+ """
+ ##Reference to first switch
+ self.switch1 = switch1
+ ##Reference to second switch
+ self.switch2 = switch2
+
+class switch:
+ """Class holding information about OpenFlow switch
+
+ Copyright(C) 2009, Stanford University
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self, miss_send_len=128,
+ sock=None, dpid=None, n_buffers=100, n_tables=1,
+ capability=None):
+ """Initialize switch
+ """
+ ##Socket to controller
+ self.sock = sock
+ ##Datapath id of switch
+ if (dpid != None):
+ self.datapath_id = dpid
+ else:
+ self.datapath_id = random.randrange(1, pow(2,64))
+ ##Number of buffers
+ self.n_buffers = n_buffers
+ ##Number of tables
+ self.n_tables= n_tables
+ ##Capabilities
+ if (isinstance(capability, switch_capabilities)):
+ self.capability = capability
+ else:
+ self.capability = switch_capabilities(miss_send_len)
+ ##Valid Actions
+ self.valid_actions = 0
+ ##List of port
+ self.port = []
+
+class switch_capabilities:
+ """Class to hold switch capabilities
+ """
+ def __init__(self, miss_send_len=128):
+ """Initialize
+
+ Copyright(C) 2009, Stanford University
+ Date October 2009
+ Created by ykk
+ """
+ ##Capabilities support by datapath
+ self.flow_stats = True
+ self.table_stats = True
+ self.port_stats = True
+ self.stp = True
+ self.multi_phy_tx = True
+ self.ip_resam = False
+ ##Switch config
+ self.send_exp = None
+ self.ip_frag = 0
+ self.miss_send_len = miss_send_len
+ ##Valid actions
+ self.act_output = True
+ self.act_set_vlan_vid = True
+ self.act_set_vlan_pcp = True
+ self.act_strip_vlan = True
+ self.act_set_dl_src = True
+ self.act_set_dl_dst = True
+ self.act_set_nw_src = True
+ self.act_set_nw_dst = True
+ self.act_set_tp_src = True
+ self.act_set_tp_dst = True
+ self.act_vendor = False
+
+ def get_capability(self, ofmsg):
+ """Return value for uint32_t capability field
+ """
+ value = 0
+ if (self.flow_stats):
+ value += ofmsg.get_value("OFPC_FLOW_STATS")
+ if (self.table_stats):
+ value += ofmsg.get_value("OFPC_TABLE_STATS")
+ if (self.port_stats):
+ value += ofmsg.get_value("OFPC_PORT_STATS")
+ if (self.stp):
+ value += ofmsg.get_value("OFPC_STP")
+ if (self.multi_phy_tx):
+ value += ofmsg.get_value("OFPC_MULTI_PHY_TX")
+ if (self.ip_resam):
+ value += ofmsg.get_value("OFPC_IP_REASM")
+ return value
+
+ def get_actions(self, ofmsg):
+ """Return value for uint32_t action field
+ """
+ value = 0
+ if (self.act_output):
+ value += (1 << (ofmsg.get_value("OFPAT_OUTPUT")+1))
+ if (self.act_set_vlan_vid):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_VLAN_VID")+1))
+ if (self.act_set_vlan_pcp):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_VLAN_PCP")+1))
+ if (self.act_strip_vlan):
+ value += (1 << (ofmsg.get_value("OFPAT_STRIP_VLAN")+1))
+ if (self.act_set_dl_src):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_DL_SRC")+1))
+ if (self.act_set_dl_dst):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_DL_DST")+1))
+ if (self.act_set_nw_src):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_NW_SRC")+1))
+ if (self.act_set_nw_dst):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_NW_DST")+1))
+ if (self.act_set_tp_src):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_TP_SRC")+1))
+ if (self.act_set_tp_dst):
+ value += (1 << (ofmsg.get_value("OFPAT_SET_TP_DST")+1))
+ return value
+
+class port:
+ """Class to hold information about port
+
+ Copyright(C) 2009, Stanford University
+ Date October 2009
+ Created by ykk
+ """
+ def __init__(self, port_no, stp=(2 << 8), hw_addr=None, name=""):
+ """Initialize
+ """
+ ##Port properties
+ self.port_no = port_no
+ if (hw_addr != None):
+ self.hw_addr = hw_addr
+ else:
+ self.hw_addr = random.randrange(1, pow(2,48))
+ self.name = name
+ ##Port config
+ self.port_down = False
+ self.no_stp = False
+ self.no_recv = False
+ self.no_recv_stp = False
+ self.no_flood = False
+ self.no_fwd = False
+ self.no_packet_in = False
+ #Port state
+ self.link_down = False
+ self.stp = stp
diff --git a/tools/pylibopenflow/pylib/of/pythonize.py b/tools/pylibopenflow/pylib/of/pythonize.py
new file mode 100644
index 0000000..687512b
--- /dev/null
+++ b/tools/pylibopenflow/pylib/of/pythonize.py
@@ -0,0 +1,57 @@
+"""This module generate Python code for OpenFlow structs.
+
+(C) Copyright Stanford University
+Date December 2009
+Created by ykk
+"""
+import cpythonize
+from config import *
+
+class rules(cpythonize.rules):
+ """Class that specify rules for pythonization of OpenFlow messages
+
+ (C) Copyright Stanford University
+ Date December 2009
+ Created by ykk
+ """
+ def __init__(self, ofmsg):
+ """Initialize rules
+ """
+ cpythonize.rules.__init__(self)
+ ##Reference to ofmsg
+ self.__ofmsg = ofmsg
+ ##Default values for members
+ self.default_values[('ofp_header','version')] = self.__ofmsg.get_value('OFP_VERSION')
+ self.default_values[('ofp_switch_config',\
+ 'miss_send_len')] = self.__ofmsg.get_value('OFP_DEFAULT_MISS_SEND_LEN')
+ for x in ['ofp_flow_mod','ofp_flow_expired','ofp_flow_stats']:
+ self.default_values[(x,'priority')] = self.__ofmsg.get_value('OFP_DEFAULT_PRIORITY')
+ #Default values for struct
+ self.struct_default[('ofp_flow_mod',
+ 'header')] = ".type = OFPT_FLOW_MOD"
+# 'header')] = ".type = "+str(self.__ofmsg.get_value('OFPT_FLOW_MOD'))
+ ##Macros to exclude
+ self.excluded_macros = ['OFP_ASSERT(EXPR)','OFP_ASSERT(_EXPR)','OFP_ASSERT',
+ 'icmp_type','icmp_code','OFP_PACKED',
+ 'OPENFLOW_OPENFLOW_H']
+ ##Enforce mapping
+ if GEN_ENUM_VALUES_LIST:
+ self.enforced_maps['ofp_header'] = [ ('type','ofp_type_values') ]
+ elif GEN_ENUM_DICTIONARY:
+ self.enforced_maps['ofp_header'] = \
+ [ ('type','ofp_type_map.values()') ]
+
+class pythonizer(cpythonize.pythonizer):
+ """Class that pythonize C structures of OpenFlow messages
+
+ (C) Copyright Stanford University
+ Date December 2009
+ Created by ykk
+ """
+ def __init__(self, ofmsg):
+ """Initialize
+ """
+ ofrules = rules(ofmsg)
+ cpythonize.pythonizer.__init__(self, ofmsg, ofrules)
+ ##Reference to OpenFlow message class
+ self.__ofmsg = ofmsg
diff --git a/tools/pylibopenflow/pylib/of/simu.py b/tools/pylibopenflow/pylib/of/simu.py
new file mode 100644
index 0000000..508b076
--- /dev/null
+++ b/tools/pylibopenflow/pylib/of/simu.py
@@ -0,0 +1,144 @@
+"""This module simulates the network.
+
+Copyright(C) 2009, Stanford University
+Date November 2009
+Created by ykk
+"""
+import openflow
+import output
+import of.msg
+import of.network
+
+class network(of.network.network):
+ """Class to simulate OpenFlow network
+
+ Copyright(C) 2009, Stanford University
+ Date November 2009
+ Created by ykk
+ """
+ def __init__(self):
+ """Initialize network
+ """
+ of.network.network.__init__(self)
+ ##Name of use for output
+ self.name = self.__class__.__name__+str(id(self))
+
+class link(of.network.link):
+ """Class to simulate link
+
+ Copyright(C) 2009, Stanford University
+ Date November 2009
+ Created by ykk
+ """
+ def __init__(self, switch1, switch2, isUp=True):
+ """Initialize link
+ """
+ of.network.link.__init__(self, switch1, switch2)
+ ##Name of use for output
+ self.name = self.__class__.__name__+str(id(self))
+ ##Indicate if link is up
+ self.isUp = isUp
+
+class switch(of.network.switch):
+ """Class to simulate OpenFlow switch
+
+ Copyright(C) 2009, Stanford University
+ Date November 2009
+ Created by ykk
+ """
+ def __init__(self, messages, controller, port, miss_send_len=128,
+ dpid=None, n_buffers=100, n_tables=1,
+ capability=None, parser=None, connection=None):
+ """Initialize switch
+ """
+ of.network.switch.__init__(self, miss_send_len,
+ None, dpid, n_buffers, n_tables,
+ capability)
+ ##Name of use for output
+ self.name = self.__class__.__name__+str(id(self))
+ ##Reference to OpenFlow messages
+ self.__messages = messages
+ ##Reference to connection
+ self.connection = openflow.tcpsocket(messages, controller, port)
+ self.sock = self.connection.sock
+ ##Reference to Parser
+ self.parser = None
+ if (parser == None):
+ self.parser = of.msg.parser(messages)
+ else:
+ self.parser = parser
+
+ def receive_openflow(self, packet):
+ """Switch receive OpenFlow packet, and respond accordingly
+ """
+ dic = self.__messages.peek_from_front("ofp_header", packet)
+ if (dic["type"][0] == self.__messages.get_value("OFPT_HELLO")):
+ output.dbg("Receive hello", self.name)
+ elif (dic["type"][0] == self.__messages.get_value("OFPT_ECHO_REQUEST")):
+ self.reply_echo(dic["xid"][0])
+ elif (dic["type"][0] == self.__messages.get_value("OFPT_FEATURES_REQUEST")):
+ self.reply_features(dic["xid"][0])
+ elif (dic["type"][0] == self.__messages.get_value("OFPT_FLOW_MOD")):
+ self.handle_flow_mod(packet)
+ else:
+ output.dbg("Unprocessed message "+self.parser.header_describe(dic),
+ self.name)
+
+ def send_hello(self):
+ """Send hello
+ """
+ self.connection.structsend("ofp_hello",
+ 0, self.__messages.get_value("OFPT_HELLO"),
+ 0, 0)
+ output.dbg("Send hello",self.name)
+
+ def send_packet(self, inport, bufferid=None, packet="", xid=0, reason=None):
+ """Send packet in
+ Assume no match as reason, bufferid = 0xFFFFFFFF,
+ and empty packet by default
+ """
+ if (reason == None):
+ reason = self.__messages.get_value("OFPR_NO_MATCH")
+ if (bufferid == None):
+ bufferid = int("0xFFFFFFFF",16)
+ pktin = self.__messages.pack("ofp_packet_in",
+ 0, self.__messages.get_value("OFPT_PACKET_IN"),
+ 0, xid, #header
+ bufferid, len(packet),
+ inport, reason, 0)
+ self.connection.structsend_raw(pktin+packet)
+ output.dbg("Send packet ",self.name)
+
+ def send_echo(self, xid=0):
+ """Send echo
+ """
+ self.connection.structsend_xid("ofp_header",
+ 0, self.__messages.get_value("OFPT_ECHO_REQUEST"),
+ 0, xid)
+ output.dbg("Send echo", self.name)
+
+ def reply_echo(self, xid):
+ """Reply to echo request
+ """
+ self.connection.structsend_xid("ofp_header",
+ 0, self.__messages.get_value("OFPT_ECHO_REPLY"),
+ 0, xid)
+ output.dbg("Reply echo of xid:"+str(xid),self.name)
+
+ def reply_features(self, xid):
+ """Reply to feature request
+ """
+ self.connection.structsend_xid("ofp_switch_features",
+ 0, self.__messages.get_value("OFPT_FEATURES_REPLY"),
+ 0, xid,
+ self.datapath_id, self.n_buffers,
+ self.n_tables,0,0,0,
+ self.capability.get_capability(self.__messages),
+ self.capability.get_actions(self.__messages))
+ output.dbg("Replied features request of xid "+str(xid), self.name)
+
+ def handle_flow_mod(self, packet):
+ """Handle flow mod: just print it here
+ """
+ output.dbg(self.parser.flow_mod_describe(packet), self.name)
+