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