blob: 25945b911f2bcf3e60cf880b323aaf9da5e8a434 [file] [log] [blame]
Dan Talaycof75360a2010-02-05 22:22:54 -08001"""This module exports OpenFlow protocol to Python.
2
3(C) Copyright Stanford University
4Date October 2009
5Created by ykk
6"""
7import c2py
8import cheader
9import os
10import socket
11import select
12import struct
13import time
14
15class messages(cheader.cheaderfile,c2py.cstruct2py,c2py.structpacker):
16 """Class to handle OpenFlow messages
17
18 (C) Copyright Stanford University
19 Date October 2009
20 Created by ykk
21 """
22 def __init__(self, openflow_headerfile=None):
23 """Initialize with OpenFlow header file
24
25 If filename is not provided, check the environment
26 variable PYLIB_OPENFLOW_HEADER and search for openflow.h
27 """
28 if (openflow_headerfile != None):
29 cheader.cheaderfile.__init__(self, openflow_headerfile)
30 else:
31 #Check environment variable
32 path = os.getenv("PYLIB_OPENFLOW_HEADER")
33 if not path:
34 print "PYLIB_OPENFLOW_HEADER is not set in environment"
35 sys.exit(2)
36 cheader.cheaderfile.__init__(self, path+"/openflow.h")
37 #Initialize cstruct2py
38 c2py.cstruct2py.__init__(self)
39 #Initalize packet
40 c2py.structpacker.__init__(self, "!")
41 ##Cached patterns
42 self.patterns={}
43 for (cstructname, cstruct) in self.structs.items():
44 self.patterns[cstructname] = self.get_pattern(cstruct)
45
46 def get_size(self, ctype):
47 """Get size for ctype or name of type.
48 Return None if ctype is not expanded or
49 type with name is not found.
50 """
51 pattern = self.get_pattern(ctype)
52 if (pattern != None):
53 return c2py.cstruct2py.get_size(self,pattern)
54
55 def get_pattern(self,ctype):
56 """Get pattern string for ctype or name of type.
57 Return None if ctype is not expanded or
58 type with name is not found.
59 """
60 if (isinstance(ctype, str)):
61 #Is name
62 return self.patterns[ctype]
63 else:
64 return c2py.cstruct2py.get_pattern(self, ctype)
65
66 def pack(self, ctype, *arg):
67 """Pack packet accordingly ctype or name of type provided.
68 Return struct packed.
69 """
70 if (isinstance(ctype, str)):
71 return struct.pack(self.prefix+self.patterns[ctype], *arg)
72 else:
73 return c2py.structpacker.pack(self, ctype, *arg)
74
75 def peek_from_front(self, ctype, binaryString, returnDictionary=True):
76 """Unpack packet using front of the packet,
77 accordingly ctype or name of ctype provided.
78
79 Return dictionary of values indexed by arg name,
80 if ctype is known struct/type and returnDictionary is True,
81 else return array of data unpacked.
82 """
83 if (isinstance(ctype,str)):
84 data = c2py.structpacker.peek_from_front(self,
85 self.patterns[ctype],
86 binaryString,
87 returnDictionary)
88 return self.data2dic(self.structs[ctype], data)
89 else:
90 return c2py.structpacker.peek_from_front(self,
91 ctype,
92 binaryString,
93 returnDictionary)
94
95 def unpack_from_front(self, ctype, binaryString, returnDictionary=True):
96 """Unpack packet using front of packet,
97 accordingly ctype or name of ctype provided.
98
99 Return (dictionary of values indexed by arg name,
100 remaining binary string) if ctype is known struct/type
101 and returnDictionary is True,
102 else return (array of data unpacked, remaining binary string).
103 """
104 if (isinstance(ctype,str)):
105 (data, remaining) = c2py.structpacker.unpack_from_front(self,
106 self.patterns[ctype],
107 binaryString,
108 returnDictionary)
109 return (self.data2dic(self.structs[ctype], data), remaining)
110 else:
111 return c2py.structpacker.unpack_from_front(self,
112 ctype,
113 binaryString,
114 returnDictionary)
115
116class connection:
117 """Class to hold a connection.
118
119 (C) Copyright Stanford University
120 Date October 2009
121 Created by ykk
122 """
123 def __init__(self, messages, sock=None):
124 """Initialize
125 """
126 ##Reference to socket
127 self.sock = sock
128 ##Internal reference to OpenFlow messages
129 self._messages = messages
130 ##Buffer
131 self.buffer = ""
132 ##Header length for OpenFlow
133 self.__header_length = self._messages.get_size("ofp_header")
134
135 def send(self, msg):
136 """Send bare message (given as binary string)
137 """
138 raise NotImplementedError()
139
140 def structsend(self, ctype, *arg):
141 """Build and send message.
142 """
143 self.send(self._messages.pack(ctype, *arg))
144
145 def receive(self, maxlength=1024):
146 """Receive raw in non-blocking way.
147
148 Return buffer
149 """
150 if (select.select([self.sock],[],[],0)[0]):
151 self.buffer += self.sock.recv(maxlength)
152 return self.buffer
153
154 def buffer_has_msg(self):
155 """Check if buffer has a complete message
156 """
157 #Check at least ofp_header is received
158 if (len(self.buffer) < self.__header_length):
159 return False
160 values = self._messages.peek_from_front("ofp_header", self.buffer)
161 return (len(self.buffer) >= values["length"][0])
162
163 def get_msg(self):
164 """Get message from current buffer
165 """
166 if (self.buffer_has_msg()):
167 values = self._messages.peek_from_front("ofp_header", self.buffer)
168 msg = self.buffer[:values["length"][0]]
169 self.buffer = self.buffer[values["length"][0]:]
170 return msg
171 else:
172 return None
173
174 def msgreceive(self, blocking=False, pollInterval=0.001):
175 """Receive OpenFlow message.
176
177 If non-blocking, can return None.
178 """
179 self.receive()
180 if (self.buffer_has_msg()):
181 return self.get_msg()
182 if (blocking):
183 while (not self.buffer_has_msg()):
184 time.sleep(pollInterval)
185 self.receive()
186 return self.get_msg()
187
188class safeconnection(connection):
189 """OpenFlow connection with safety checks
190
191 (C) Copyright Stanford University
192 Date October 2009
193 Created by ykk
194 """
195 def __init__(self, messages, sock=None, version=None,
196 xidstart = 0, autoxid=True):
197 """Initialize with OpenFlow version.
198 """
199 connection.__init__(self, messages, sock)
200 ##OpenFlow version
201 if (version != None):
202 self.version = version
203 else:
204 self.version = messages.get_value("OFP_VERSION")
205 ##xid Counter
206 self.nextxid = xidstart
207 ##Automatic xid
208 self.autoxid = autoxid
209 ##Miss auto xid
210 self.skipautoxid = 0
211
212 def skip_auto_xid(self, n):
213 """Miss automatic xid for the next n packets
214 """
215 self.skipautoxid = n
216
217 def structsend_xid(self, ctype, *arg):
218 """Build and send message, populating header automatically.
219 Type and xid of message is not populated.
220 """
221 self.skipautoxid+=1
222 self.structsend(ctype, *arg)
223
224 def structsend(self, ctype, *arg):
225 """Build and send message, populating header automatically.
226 Type of message is not populated
227 """
228 msg = self._messages.pack(ctype, *arg)
229 self.structsend_raw(msg)
230
231 def structsend_raw(self, msg):
232 """Check ofp_header and ensure correctness before sending.
233 """
234 (dic, remaining) = self._messages.unpack_from_front("ofp_header", msg)
235 #Amend header
236 if (self.version != None):
237 dic["version"][0] = self.version
238 if (self.autoxid and (self.skipautoxid == 0)):
239 dic["xid"][0] = self.nextxid
240 self.nextxid+=1
241 if (self.skipautoxid != 0):
242 self.skipautoxid-=1
243 dic["length"][0] = len(remaining)+8
244 #Send message
245 self.send(self._messages.pack("ofp_header",
246 dic["version"][0],
247 dic["type"][0],
248 dic["length"][0],
249 dic["xid"][0])+\
250 remaining)
251
252class tcpsocket(safeconnection):
253 """Class to hold connection
254
255 (C) Copyright Stanford University
256 Date October 2009
257 Created by ykk
258 """
259 def __init__(self, messages, host, port):
260 """Initialize TCP socket to host and port
261 """
262 safeconnection.__init__(self, messages)
263 ##Reference to socket
264 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
265 self.sock.connect((host, port))
266 self.sock.setblocking(False)
267 self.sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 0)
268
269 def __del__(self):
270 """Terminate connection
271 """
272 self.sock.shutdown(1)
273 self.sock.close()
274
275 def send(self, msg):
276 """Send raw message (binary string)
277 """
278 self.sock.sendall(msg)
279
280class connections:
281 """Class to hold multiple connections
282
283 (C) Copyright Stanford University
284 Date November 2009
285 Created by ykk
286 """
287 def __init__(self):
288 """Initialize
289 """
290 ##List of sockets
291 self.__sockets = []
292 ##Dicionary of sockets to connection
293 self.__connections = {}
294
295 def add_connection(self, reference, connect):
296 """Add connection with opaque reference object
297 """
298 if (not isinstance(connect,connection)):
299 raise RuntimeError("Connection must be openflow.connection!")
300 self.__sockets.append(connect.sock)
301 self.__connections[connect.sock] = (reference, connect)
302
303 def receive(self, maxlength=1024):
304 """Receive raw in non-blocking way
305 """
306 read_ready = select.select(self.__sockets,[],[],0)[0]
307 for sock in read_ready:
308 self.__connections[sock][1].receive(maxlength)
309
310 def has_msg(self):
311 """Check if any of the connections has a message
312
313 Return (reference,connection) with message
314 """
315 for sock, refconnect in self.__connections.items():
316 if (refconnect[1].buffer_has_msg()):
317 return refconnect
318 return None
319
320 def msgreceive(self, blocking=False, pollInterval=0.001):
321 """Receive OpenFlow message.
322
323 If non-blocking, can return None.
324 """
325 self.receive()
326 c = self.has_msg()
327 if (c != None):
328 return (c[0],c[1].get_msg())
329 if (blocking):
330 while (c == None):
331 time.sleep(pollInterval)
332 self.receive()
333 c = self.has_msg()
334 else:
335 return (None, None)
336 return (c[0],c[1].get_msg())