1 # Copyright (C) 2011, 2012 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2011 Isaku Yamahata <yamahata at valinux co jp>
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
8 # http://www.apache.org/licenses/LICENSE-2.0
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
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
25 from ryu import exception
27 from ryu.lib import stringify
29 from ryu.ofproto import ofproto_common
31 LOG = logging.getLogger('ryu.ofproto.ofproto_parser')
33 # This is merely for API compatibility on python2
39 assert len(buf) >= ofproto_common.OFP_HEADER_SIZE
40 # LOG.debug('len %d bufsize %d', len(buf), ofproto.OFP_HEADER_SIZE)
41 return struct.unpack_from(ofproto_common.OFP_HEADER_PACK_STR,
48 def register_msg_parser(version):
49 def register(msg_parser):
50 _MSG_PARSERS[version] = msg_parser
55 def msg(datapath, version, msg_type, msg_len, xid, buf):
58 assert len(buf) >= msg_len
59 except AssertionError as e:
62 msg_parser = _MSG_PARSERS.get(version)
63 if msg_parser is None:
64 raise exception.OFPUnknownVersion(version=version)
67 msg = msg_parser(datapath, version, msg_type, msg_len, xid, buf)
68 except exception.OFPTruncatedMessage as e:
72 'Encountered an error while parsing OpenFlow packet from switch. '
73 'This implies the switch sent a malformed OpenFlow packet. '
74 'version 0x%02x msg_type %d msg_len %d xid %d buf %s',
75 version, msg_type, msg_len, xid, utils.hex_array(buf))
82 def create_list_of_base_attributes(f):
84 def wrapper(self, *args, **kwargs):
85 ret = f(self, *args, **kwargs)
87 # hasattr(cls, '_base_attributes') doesn't work because super class
88 # may already have the attribute.
89 if '_base_attributes' not in cls.__dict__:
90 cls._base_attributes = set(dir(self))
95 def ofp_msg_from_jsondict(dp, jsondict):
97 This function instanticates an appropriate OpenFlow message class
98 from the given JSON style dictionary.
99 The objects created by following two code fragments are equivalent.
103 jsonstr = '{ "OFPSetConfig": { "flags": 0, "miss_send_len": 128 } }'
104 jsondict = json.loads(jsonstr)
105 o = ofp_msg_from_jsondict(dp, jsondict)
109 o = dp.ofproto_parser.OFPSetConfig(flags=0, miss_send_len=128)
111 This function takes the following arguments.
113 ======== =======================================
115 ======== =======================================
116 dp An instance of ryu.controller.Datapath.
117 jsondict A JSON style dict.
118 ======== =======================================
120 parser = dp.ofproto_parser
121 assert len(jsondict) == 1
122 for k, v in jsondict.items():
123 cls = getattr(parser, k)
124 assert issubclass(cls, MsgBase)
125 return cls.from_jsondict(v, datapath=dp)
128 def ofp_instruction_from_jsondict(dp, jsonlist, encap=True):
130 This function is intended to be used with
131 ryu.lib.ofctl_string.ofp_instruction_from_str.
132 It is very similar to ofp_msg_from_jsondict, but works on
133 a list of OFPInstructions/OFPActions. It also encapsulates
134 OFPAction into OFPInstructionActions, as >OF1.0 OFPFlowMod
137 This function takes the following arguments.
139 ======== ==================================================
141 ======== ==================================================
142 dp An instance of ryu.controller.Datapath.
143 jsonlist A list of JSON style dictionaries.
144 encap Encapsulate OFPAction into OFPInstructionActions.
145 Must be false for OF10.
146 ======== ==================================================
149 parser = dp.ofproto_parser
152 for jsondict in jsonlist:
153 assert len(jsondict) == 1
154 k, v = list(jsondict.items())[0]
155 cls = getattr(parser, k)
156 if issubclass(cls, parser.OFPAction):
158 actions.append(cls.from_jsondict(v))
161 ofpinst = getattr(parser, 'OFPInstruction', None)
162 if not ofpinst or not issubclass(cls, ofpinst):
163 raise ValueError("Supplied jsondict is of wrong type: %s",
165 result.append(cls.from_jsondict(v))
171 # Although the OpenFlow spec says Apply Actions is executed first,
172 # let's place it in the head as a precaution.
173 result = [parser.OFPInstructionActions(
174 proto.OFPIT_APPLY_ACTIONS, actions)] + result
178 class StringifyMixin(stringify.StringifyMixin):
179 _class_prefixes = ["OFP", "ONF", "MT", "NX"]
182 def cls_from_jsondict_key(cls, k):
183 obj_cls = super(StringifyMixin, cls).cls_from_jsondict_key(k)
187 class MsgBase(StringifyMixin):
189 This is a base class for OpenFlow message classes.
191 An instance of this class has at least the following attributes.
193 ========= ==============================
194 Attribute Description
195 ========= ==============================
196 datapath A ryu.controller.controller.Datapath instance for this message
197 version OpenFlow protocol version
198 msg_type Type of OpenFlow message
199 msg_len Length of the message
202 ========= ==============================
205 @create_list_of_base_attributes
206 def __init__(self, datapath):
207 super(MsgBase, self).__init__()
208 self.datapath = datapath
215 def set_headers(self, version, msg_type, msg_len, xid):
216 assert msg_type == self.cls_msg_type
218 self.version = version
219 self.msg_type = msg_type
220 self.msg_len = msg_len
223 def set_xid(self, xid):
224 assert self.xid is None
227 def set_buf(self, buf):
228 self.buf = buffer(buf)
232 return hex(x) if isinstance(x, six.integer_types) else x
233 buf = 'version=%s,msg_type=%s,msg_len=%s,xid=%s,' %\
234 (hexify(self.version), hexify(self.msg_type),
235 hexify(self.msg_len), hexify(self.xid))
236 return buf + StringifyMixin.__str__(self)
239 def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
241 msg_.set_headers(version, msg_type, msg_len, xid)
245 def _serialize_pre(self):
246 self.version = self.datapath.ofproto.OFP_VERSION
247 self.msg_type = self.cls_msg_type
248 self.buf = bytearray(self.datapath.ofproto.OFP_HEADER_SIZE)
250 def _serialize_header(self):
251 # buffer length is determined after trailing data is formated.
252 assert self.version is not None
253 assert self.msg_type is not None
254 assert self.buf is not None
255 assert len(self.buf) >= self.datapath.ofproto.OFP_HEADER_SIZE
257 self.msg_len = len(self.buf)
261 struct.pack_into(self.datapath.ofproto.OFP_HEADER_PACK_STR,
263 self.version, self.msg_type, self.msg_len, self.xid)
265 def _serialize_body(self):
269 self._serialize_pre()
270 self._serialize_body()
271 self._serialize_header()
274 class MsgInMsgBase(MsgBase):
276 def _decode_value(cls, k, json_value, decode_string=base64.b64decode,
278 return cls._get_decoder(k, decode_string)(json_value,
282 def namedtuple(typename, fields, **kwargs):
283 class _namedtuple(StringifyMixin,
284 collections.namedtuple(typename, fields, **kwargs)):
289 def msg_str_attr(msg_, buf, attr_list=None):
290 if attr_list is None:
291 attr_list = stringify.obj_attrs(msg_)
292 for attr in attr_list:
293 val = getattr(msg_, attr, None)
295 buf += ' %s %s' % (attr, val)