| # Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University |
| # Copyright (c) 2011, 2012 Open Networking Foundation |
| # Copyright (c) 2012, 2013 Big Switch Networks, Inc. |
| # See the file LICENSE.pyloxi which should have been included in the source distribution |
| """ |
| Utility functions independent of the protocol version |
| """ |
| |
| # Automatically generated by LOXI from template generic_util.py |
| # Do not modify |
| |
| import loxi |
| import struct |
| |
| def unpack_list(reader, deserializer): |
| """ |
| The deserializer function should take an OFReader and return the new object. |
| """ |
| entries = [] |
| while not reader.is_empty(): |
| entries.append(deserializer(reader)) |
| return entries |
| |
| def unpack_list_lv16(reader, deserializer): |
| """ |
| The deserializer function should take an OFReader and return the new object. |
| """ |
| def wrapper(reader): |
| length, = reader.peek('!H') |
| return deserializer(reader.slice(length)) |
| return unpack_list(reader, wrapper) |
| |
| def unpack_list_tlv16(reader, deserializer): |
| """ |
| The deserializer function should take an OFReader and an integer type |
| and return the new object. |
| """ |
| def wrapper(reader): |
| typ, length, = reader.peek('!HH') |
| return deserializer(reader.slice(length), typ) |
| return unpack_list(reader, wrapper) |
| |
| def pad_to(alignment, length): |
| """ |
| Return a string of zero bytes that will pad a string of length 'length' to |
| a multiple of 'alignment'. |
| """ |
| return "\x00" * ((length + alignment - 1)/alignment*alignment - length) |
| |
| class OFReader(object): |
| """ |
| Cursor over a read-only buffer |
| |
| OpenFlow messages are best thought of as a sequence of elements of |
| variable size, rather than a C-style struct with fixed offsets and |
| known field lengths. This class supports efficiently reading |
| fields sequentially and is intended to be used recursively by the |
| parsers of child objects which will implicitly update the offset. |
| """ |
| def __init__(self, buf): |
| self.buf = buf |
| self.offset = 0 |
| |
| def read(self, fmt): |
| st = struct.Struct(fmt) |
| if self.offset + st.size > len(self.buf): |
| raise loxi.ProtocolError("Buffer too short") |
| result = st.unpack_from(self.buf, self.offset) |
| self.offset += st.size |
| return result |
| |
| def read_all(self): |
| buf = buffer(self.buf, self.offset) |
| self.offset += len(buf) |
| return str(buf) |
| |
| def peek(self, fmt): |
| st = struct.Struct(fmt) |
| if self.offset + st.size > len(self.buf): |
| raise loxi.ProtocolError("Buffer too short") |
| result = st.unpack_from(self.buf, self.offset) |
| return result |
| |
| def skip(self, length): |
| if self.offset + length > len(self.buf): |
| raise loxi.ProtocolError("Buffer too short") |
| self.offset += length |
| |
| def skip_align(self): |
| new_offset = (self.offset + 7) / 8 * 8 |
| if new_offset > len(self.buf): |
| raise loxi.ProtocolError("Buffer too short") |
| self.offset = new_offset |
| |
| def is_empty(self): |
| return self.offset == len(self.buf) |
| |
| # Used when parsing variable length objects which have external length |
| # fields (e.g. the actions list in an OF 1.0 packet-out message). |
| def slice(self, length): |
| if self.offset + length > len(self.buf): |
| raise loxi.ProtocolError("Buffer too short") |
| buf = OFReader(buffer(self.buf, self.offset, length)) |
| self.offset += length |
| return buf |