blob: 7cbf32ede3b024c0e0decc059291650bab139c44 [file] [log] [blame]
Zack Williams41513bf2018-07-07 20:08:35 -07001# Copyright 2017-present Open Networking Foundation
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
Nathan Knuth418fdc82016-09-16 22:51:15 -070014# Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University
15# Copyright (c) 2011, 2012 Open Networking Foundation
16# Copyright (c) 2012, 2013 Big Switch Networks, Inc.
17# See the file LICENSE.pyloxi which should have been included in the source distribution
18"""
19Utility functions independent of the protocol version
20"""
21
22# Automatically generated by LOXI from template generic_util.py
23# Do not modify
24
25import loxi
26import struct
27
28def pack_list(values):
29 return "".join([x.pack() for x in values])
30
31def unpack_list(reader, deserializer):
32 """
33 The deserializer function should take an OFReader and return the new object.
34 """
35 entries = []
36 while not reader.is_empty():
37 entries.append(deserializer(reader))
38 return entries
39
40def pad_to(alignment, length):
41 """
42 Return a string of zero bytes that will pad a string of length 'length' to
43 a multiple of 'alignment'.
44 """
45 return "\x00" * ((length + alignment - 1)/alignment*alignment - length)
46
47class OFReader(object):
48 """
49 Cursor over a read-only buffer
50
51 OpenFlow messages are best thought of as a sequence of elements of
52 variable size, rather than a C-style struct with fixed offsets and
53 known field lengths. This class supports efficiently reading
54 fields sequentially and is intended to be used recursively by the
55 parsers of child objects which will implicitly update the offset.
56
57 buf: buffer object
58 start: initial position in the buffer
59 length: number of bytes after start
60 offset: distance from start
61 """
62 def __init__(self, buf, start=0, length=None):
63 self.buf = buf
64 self.start = start
65 if length is None:
66 self.length = len(buf) - start
67 else:
68 self.length = length
69 self.offset = 0
70
71 def read(self, fmt):
72 st = struct.Struct(fmt)
73 if self.offset + st.size > self.length:
74 raise loxi.ProtocolError("Buffer too short")
75 result = st.unpack_from(self.buf, self.start+self.offset)
76 self.offset += st.size
77 return result
78
79 def read_all(self):
80 s = self.buf[(self.start+self.offset):(self.start+self.length)]
81 assert(len(s) == self.length - self.offset)
82 self.offset = self.length
83 return s
84
85 def peek(self, fmt, offset=0):
86 st = struct.Struct(fmt)
87 if self.offset + offset + st.size > self.length:
88 raise loxi.ProtocolError("Buffer too short")
89 result = st.unpack_from(self.buf, self.start + self.offset + offset)
90 return result
91
92 def skip(self, length):
93 if self.offset + length > self.length:
94 raise loxi.ProtocolError("Buffer too short")
95 self.offset += length
96
97 def skip_align(self):
98 new_offset = (self.offset + 7) / 8 * 8
99 if new_offset > self.length:
100 raise loxi.ProtocolError("Buffer too short")
101 self.offset = new_offset
102
103 def is_empty(self):
104 return self.offset == self.length
105
106 # Used when parsing objects that have their own length fields
107 def slice(self, length, rewind=0):
108 if self.offset + length - rewind > self.length:
109 raise loxi.ProtocolError("Buffer too short")
110 reader = OFReader(self.buf, self.start + self.offset - rewind, length)
111 reader.skip(rewind)
112 self.offset += length - rewind
113 return reader