blob: 25945b911f2bcf3e60cf880b323aaf9da5e8a434 [file] [log] [blame]
"""This module exports OpenFlow protocol to Python.
(C) Copyright Stanford University
Date October 2009
Created by ykk
"""
import c2py
import cheader
import os
import socket
import select
import struct
import time
class messages(cheader.cheaderfile,c2py.cstruct2py,c2py.structpacker):
"""Class to handle OpenFlow messages
(C) Copyright Stanford University
Date October 2009
Created by ykk
"""
def __init__(self, openflow_headerfile=None):
"""Initialize with OpenFlow header file
If filename is not provided, check the environment
variable PYLIB_OPENFLOW_HEADER and search for openflow.h
"""
if (openflow_headerfile != None):
cheader.cheaderfile.__init__(self, openflow_headerfile)
else:
#Check environment variable
path = os.getenv("PYLIB_OPENFLOW_HEADER")
if not path:
print "PYLIB_OPENFLOW_HEADER is not set in environment"
sys.exit(2)
cheader.cheaderfile.__init__(self, path+"/openflow.h")
#Initialize cstruct2py
c2py.cstruct2py.__init__(self)
#Initalize packet
c2py.structpacker.__init__(self, "!")
##Cached patterns
self.patterns={}
for (cstructname, cstruct) in self.structs.items():
self.patterns[cstructname] = self.get_pattern(cstruct)
def get_size(self, ctype):
"""Get size for ctype or name of type.
Return None if ctype is not expanded or
type with name is not found.
"""
pattern = self.get_pattern(ctype)
if (pattern != None):
return c2py.cstruct2py.get_size(self,pattern)
def get_pattern(self,ctype):
"""Get pattern string for ctype or name of type.
Return None if ctype is not expanded or
type with name is not found.
"""
if (isinstance(ctype, str)):
#Is name
return self.patterns[ctype]
else:
return c2py.cstruct2py.get_pattern(self, ctype)
def pack(self, ctype, *arg):
"""Pack packet accordingly ctype or name of type provided.
Return struct packed.
"""
if (isinstance(ctype, str)):
return struct.pack(self.prefix+self.patterns[ctype], *arg)
else:
return c2py.structpacker.pack(self, ctype, *arg)
def peek_from_front(self, ctype, binaryString, returnDictionary=True):
"""Unpack packet using front of the packet,
accordingly ctype or name of ctype provided.
Return dictionary of values indexed by arg name,
if ctype is known struct/type and returnDictionary is True,
else return array of data unpacked.
"""
if (isinstance(ctype,str)):
data = c2py.structpacker.peek_from_front(self,
self.patterns[ctype],
binaryString,
returnDictionary)
return self.data2dic(self.structs[ctype], data)
else:
return c2py.structpacker.peek_from_front(self,
ctype,
binaryString,
returnDictionary)
def unpack_from_front(self, ctype, binaryString, returnDictionary=True):
"""Unpack packet using front of packet,
accordingly ctype or name of ctype provided.
Return (dictionary of values indexed by arg name,
remaining binary string) if ctype is known struct/type
and returnDictionary is True,
else return (array of data unpacked, remaining binary string).
"""
if (isinstance(ctype,str)):
(data, remaining) = c2py.structpacker.unpack_from_front(self,
self.patterns[ctype],
binaryString,
returnDictionary)
return (self.data2dic(self.structs[ctype], data), remaining)
else:
return c2py.structpacker.unpack_from_front(self,
ctype,
binaryString,
returnDictionary)
class connection:
"""Class to hold a connection.
(C) Copyright Stanford University
Date October 2009
Created by ykk
"""
def __init__(self, messages, sock=None):
"""Initialize
"""
##Reference to socket
self.sock = sock
##Internal reference to OpenFlow messages
self._messages = messages
##Buffer
self.buffer = ""
##Header length for OpenFlow
self.__header_length = self._messages.get_size("ofp_header")
def send(self, msg):
"""Send bare message (given as binary string)
"""
raise NotImplementedError()
def structsend(self, ctype, *arg):
"""Build and send message.
"""
self.send(self._messages.pack(ctype, *arg))
def receive(self, maxlength=1024):
"""Receive raw in non-blocking way.
Return buffer
"""
if (select.select([self.sock],[],[],0)[0]):
self.buffer += self.sock.recv(maxlength)
return self.buffer
def buffer_has_msg(self):
"""Check if buffer has a complete message
"""
#Check at least ofp_header is received
if (len(self.buffer) < self.__header_length):
return False
values = self._messages.peek_from_front("ofp_header", self.buffer)
return (len(self.buffer) >= values["length"][0])
def get_msg(self):
"""Get message from current buffer
"""
if (self.buffer_has_msg()):
values = self._messages.peek_from_front("ofp_header", self.buffer)
msg = self.buffer[:values["length"][0]]
self.buffer = self.buffer[values["length"][0]:]
return msg
else:
return None
def msgreceive(self, blocking=False, pollInterval=0.001):
"""Receive OpenFlow message.
If non-blocking, can return None.
"""
self.receive()
if (self.buffer_has_msg()):
return self.get_msg()
if (blocking):
while (not self.buffer_has_msg()):
time.sleep(pollInterval)
self.receive()
return self.get_msg()
class safeconnection(connection):
"""OpenFlow connection with safety checks
(C) Copyright Stanford University
Date October 2009
Created by ykk
"""
def __init__(self, messages, sock=None, version=None,
xidstart = 0, autoxid=True):
"""Initialize with OpenFlow version.
"""
connection.__init__(self, messages, sock)
##OpenFlow version
if (version != None):
self.version = version
else:
self.version = messages.get_value("OFP_VERSION")
##xid Counter
self.nextxid = xidstart
##Automatic xid
self.autoxid = autoxid
##Miss auto xid
self.skipautoxid = 0
def skip_auto_xid(self, n):
"""Miss automatic xid for the next n packets
"""
self.skipautoxid = n
def structsend_xid(self, ctype, *arg):
"""Build and send message, populating header automatically.
Type and xid of message is not populated.
"""
self.skipautoxid+=1
self.structsend(ctype, *arg)
def structsend(self, ctype, *arg):
"""Build and send message, populating header automatically.
Type of message is not populated
"""
msg = self._messages.pack(ctype, *arg)
self.structsend_raw(msg)
def structsend_raw(self, msg):
"""Check ofp_header and ensure correctness before sending.
"""
(dic, remaining) = self._messages.unpack_from_front("ofp_header", msg)
#Amend header
if (self.version != None):
dic["version"][0] = self.version
if (self.autoxid and (self.skipautoxid == 0)):
dic["xid"][0] = self.nextxid
self.nextxid+=1
if (self.skipautoxid != 0):
self.skipautoxid-=1
dic["length"][0] = len(remaining)+8
#Send message
self.send(self._messages.pack("ofp_header",
dic["version"][0],
dic["type"][0],
dic["length"][0],
dic["xid"][0])+\
remaining)
class tcpsocket(safeconnection):
"""Class to hold connection
(C) Copyright Stanford University
Date October 2009
Created by ykk
"""
def __init__(self, messages, host, port):
"""Initialize TCP socket to host and port
"""
safeconnection.__init__(self, messages)
##Reference to socket
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((host, port))
self.sock.setblocking(False)
self.sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 0)
def __del__(self):
"""Terminate connection
"""
self.sock.shutdown(1)
self.sock.close()
def send(self, msg):
"""Send raw message (binary string)
"""
self.sock.sendall(msg)
class connections:
"""Class to hold multiple connections
(C) Copyright Stanford University
Date November 2009
Created by ykk
"""
def __init__(self):
"""Initialize
"""
##List of sockets
self.__sockets = []
##Dicionary of sockets to connection
self.__connections = {}
def add_connection(self, reference, connect):
"""Add connection with opaque reference object
"""
if (not isinstance(connect,connection)):
raise RuntimeError("Connection must be openflow.connection!")
self.__sockets.append(connect.sock)
self.__connections[connect.sock] = (reference, connect)
def receive(self, maxlength=1024):
"""Receive raw in non-blocking way
"""
read_ready = select.select(self.__sockets,[],[],0)[0]
for sock in read_ready:
self.__connections[sock][1].receive(maxlength)
def has_msg(self):
"""Check if any of the connections has a message
Return (reference,connection) with message
"""
for sock, refconnect in self.__connections.items():
if (refconnect[1].buffer_has_msg()):
return refconnect
return None
def msgreceive(self, blocking=False, pollInterval=0.001):
"""Receive OpenFlow message.
If non-blocking, can return None.
"""
self.receive()
c = self.has_msg()
if (c != None):
return (c[0],c[1].get_msg())
if (blocking):
while (c == None):
time.sleep(pollInterval)
self.receive()
c = self.has_msg()
else:
return (None, None)
return (c[0],c[1].get_msg())