blob: 7cbf32ede3b024c0e0decc059291650bab139c44 [file] [log] [blame]
# Copyright 2017-present Open Networking Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 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 pack_list(values):
return "".join([x.pack() for x in values])
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 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.
buf: buffer object
start: initial position in the buffer
length: number of bytes after start
offset: distance from start
"""
def __init__(self, buf, start=0, length=None):
self.buf = buf
self.start = start
if length is None:
self.length = len(buf) - start
else:
self.length = length
self.offset = 0
def read(self, fmt):
st = struct.Struct(fmt)
if self.offset + st.size > self.length:
raise loxi.ProtocolError("Buffer too short")
result = st.unpack_from(self.buf, self.start+self.offset)
self.offset += st.size
return result
def read_all(self):
s = self.buf[(self.start+self.offset):(self.start+self.length)]
assert(len(s) == self.length - self.offset)
self.offset = self.length
return s
def peek(self, fmt, offset=0):
st = struct.Struct(fmt)
if self.offset + offset + st.size > self.length:
raise loxi.ProtocolError("Buffer too short")
result = st.unpack_from(self.buf, self.start + self.offset + offset)
return result
def skip(self, length):
if self.offset + length > self.length:
raise loxi.ProtocolError("Buffer too short")
self.offset += length
def skip_align(self):
new_offset = (self.offset + 7) / 8 * 8
if new_offset > self.length:
raise loxi.ProtocolError("Buffer too short")
self.offset = new_offset
def is_empty(self):
return self.offset == self.length
# Used when parsing objects that have their own length fields
def slice(self, length, rewind=0):
if self.offset + length - rewind > self.length:
raise loxi.ProtocolError("Buffer too short")
reader = OFReader(self.buf, self.start + self.offset - rewind, length)
reader.skip(rewind)
self.offset += length - rewind
return reader