diff options
author | YAMAMOTO Takashi <yamamoto@valinux.co.jp> | 2013-10-28 17:08:10 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2013-10-28 23:02:49 +0900 |
commit | 5769688e32dfe0a28bb148abd5f7670b10624b40 (patch) | |
tree | c9888c55c738b51e1c42ab36c64f4a6302ec9ac1 | |
parent | f138351288f10f8fb4a9308cbda91a7b5d126d66 (diff) |
of13: implement table features request/reply
Signed-off-by: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r-- | ryu/ofproto/ofproto_v1_3_parser.py | 321 |
1 files changed, 306 insertions, 15 deletions
diff --git a/ryu/ofproto/ofproto_v1_3_parser.py b/ryu/ofproto/ofproto_v1_3_parser.py index d3db3fbe..50e69923 100644 --- a/ryu/ofproto/ofproto_v1_3_parser.py +++ b/ryu/ofproto/ofproto_v1_3_parser.py @@ -4733,13 +4733,308 @@ class OFPTableFeaturesStats(StringifyMixin): ) = struct.unpack_from(ofproto_v1_3.OFP_TABLE_FEATURES_PACK_STR, buf, offset) table_features.name = name.rstrip('\0') - offset += ofproto_v1_3.OFP_TABLE_FEATURES_SIZE - # TODO: parse ofp_table_feature_prop_header - table_features.properties = [] + props = [] + rest = buf[offset + ofproto_v1_3.OFP_TABLE_FEATURES_SIZE: + offset + table_features.length] + while rest: + p, rest = OFPTableFeatureProp.parse(rest) + props.append(p) + table_features.properties = props return table_features + def serialize(self): + # fixup + bin_props = bytearray() + for p in self.properties: + bin_props += p.serialize() + self.length = ofproto_v1_3.OFP_TABLE_FEATURES_SIZE + len(bin_props) + + buf = bytearray() + msg_pack_into(ofproto_v1_3.OFP_TABLE_FEATURES_PACK_STR, buf, 0, + self.length, self.table_id, self.name, + self.metadata_match, self.metadata_write, + self.config, self.max_entries) + return buf + bin_props + + +class OFPTableFeatureProp(StringifyMixin): + _PACK_STR = '!HH' # type, length + _TYPES = {} # OFPTFPT_ -> class + + def __init__(self, type_, length=None): + self.type = type_ + self.length = length + + @classmethod + def register_type(cls, type_): + def _register_type(subcls): + cls._TYPES[type_] = subcls + return subcls + return _register_type + + @classmethod + def parse(cls, buf): + (type_, length,) = struct.unpack_from(cls._PACK_STR, buffer(buf), 0) + bin_prop = buf[struct.calcsize(cls._PACK_STR):length] + rest = buf[utils.round_up(length, 8):] + try: + subcls = cls._TYPES[type_] + except KeyError: + subcls = OFPTableFeaturePropUnknown + kwargs = subcls._parse_prop(bin_prop) + kwargs['type_'] = type_ + kwargs['length'] = length + return subcls(**kwargs), rest + + def serialize(self): + # fixup + bin_prop = self._serialize_prop() + self.length = struct.calcsize(self._PACK_STR) + len(bin_prop) + + buf = bytearray() + msg_pack_into(self._PACK_STR, buf, 0, self.type, self.length) + pad_len = utils.round_up(self.length, 8) - self.length + return buf + bin_prop + pad_len * '\0' + + +class OFPTableFeaturePropUnknown(OFPTableFeatureProp): + def __init__(self, type_, length=None, data=None): + super(OFPTableFeaturePropUnknown, self).__init__(type_, length) + self.data = data + + @classmethod + def _parse_prop(cls, buf): + return {'data': buf} + + def _serialize_prop(self): + return self.data + + +# Implementation note: While OpenFlow 1.3.2 shares the same ofp_instruction +# for flow_mod and table_features, we have separate classes. We named this +# class to match with OpenFlow 1.4's name. (ofp_instruction_id) +class OFPInstructionId(StringifyMixin): + _PACK_STR = '!HH' # type, len + + def __init__(self, type_, len_=None): + self.type = type_ + self.len = len_ + # XXX experimenter + + @classmethod + def parse(cls, buf): + (type_, len_,) = struct.unpack_from(cls._PACK_STR, buffer(buf), 0) + rest = buf[len_:] + return cls(type_=type_, len_=len_), rest + + def serialize(self): + # fixup + self.len = struct.calcsize(self._PACK_STR) + + buf = bytearray() + msg_pack_into(self._PACK_STR, buf, 0, self.type, self.len) + return buf + + +@OFPTableFeatureProp.register_type(ofproto_v1_3.OFPTFPT_INSTRUCTIONS) +@OFPTableFeatureProp.register_type(ofproto_v1_3.OFPTFPT_INSTRUCTIONS_MISS) +class OFPTableFeaturePropInstructions(OFPTableFeatureProp): + def __init__(self, type_, instruction_ids=[], length=None): + super(OFPTableFeaturePropInstructions, self).__init__(type_, length) + self.instruction_ids = instruction_ids + + @classmethod + def _parse_prop(cls, buf): + rest = buf + ids = [] + while rest: + i, rest = OFPInstructionId.parse(rest) + ids.append(i) + return { + 'instruction_ids': ids, + } + + def _serialize_prop(self): + bin_ids = bytearray() + for i in self.instruction_ids: + bin_ids += i.serialize() + return bin_ids + + +@OFPTableFeatureProp.register_type(ofproto_v1_3.OFPTFPT_NEXT_TABLES) +@OFPTableFeatureProp.register_type(ofproto_v1_3.OFPTFPT_NEXT_TABLES_MISS) +class OFPTableFeaturePropNextTables(OFPTableFeatureProp): + _TABLE_ID_PACK_STR = '!B' + + def __init__(self, type_, table_ids=[], length=None): + super(OFPTableFeaturePropNextTables, self).__init__(type_, length) + self.table_ids = table_ids + + @classmethod + def _parse_prop(cls, buf): + rest = buf + ids = [] + while rest: + (i,) = struct.unpack_from(cls._TABLE_ID_PACK_STR, buffer(rest), 0) + rest = rest[struct.calcsize(cls._TABLE_ID_PACK_STR):] + ids.append(i) + return { + 'table_ids': ids, + } + + def _serialize_prop(self): + bin_ids = bytearray() + for i in self.table_ids: + bin_id = bytearray() + msg_pack_into(self._TABLE_ID_PACK_STR, bin_id, 0, i) + bin_ids += bin_id + return bin_ids + + +# Implementation note: While OpenFlow 1.3.2 shares the same ofp_action_header +# for flow_mod and table_features, we have separate classes. We named this +# class to match with OpenFlow 1.4's name. (ofp_action_id) +class OFPActionId(StringifyMixin): + # XXX + # ofp_action_header should have trailing pad bytes. + # however, i guess it's a specification bug as: + # - the spec explicitly says non-experimenter actions are 4 bytes + # - linc/of_protocol doesn't use them + # - OpenFlow 1.4 changed to use a separate structure + _PACK_STR = '!HH' # type, len + + def __init__(self, type_, len_=None): + self.type = type_ + self.len = len_ + # XXX experimenter + + @classmethod + def parse(cls, buf): + (type_, len_,) = struct.unpack_from(cls._PACK_STR, buffer(buf), 0) + rest = buf[len_:] + return cls(type_=type_, len_=len_), rest + + def serialize(self): + # fixup + self.len = struct.calcsize(self._PACK_STR) + + buf = bytearray() + msg_pack_into(self._PACK_STR, buf, 0, self.type, self.len) + return buf + + +@OFPTableFeatureProp.register_type(ofproto_v1_3.OFPTFPT_WRITE_ACTIONS) +@OFPTableFeatureProp.register_type(ofproto_v1_3.OFPTFPT_WRITE_ACTIONS_MISS) +@OFPTableFeatureProp.register_type(ofproto_v1_3.OFPTFPT_APPLY_ACTIONS) +@OFPTableFeatureProp.register_type(ofproto_v1_3.OFPTFPT_APPLY_ACTIONS_MISS) +class OFPTableFeaturePropActions(OFPTableFeatureProp): + def __init__(self, type_, action_ids=[], length=None): + super(OFPTableFeaturePropActions, self).__init__(type_, length) + self.action_ids = action_ids + + @classmethod + def _parse_prop(cls, buf): + rest = buf + ids = [] + while rest: + i, rest = OFPActionId.parse(rest) + ids.append(i) + return { + 'action_ids': ids, + } + + def _serialize_prop(self): + bin_ids = bytearray() + for i in self.action_ids: + bin_ids += i.serialize() + return bin_ids + + +# Implementation note: OFPOxmId is specific to this implementation. +# It does not have a corresponding structure in the specification. +# (the specification uses plain uint32_t for them.) +# +# i have taken a look at some of software switch implementations +# but they all look broken or incomplete. according to the spec, +# oxm_hasmask should be 1 if a switch supports masking for the type. +# the right value for oxm_length is not clear from the spec. +# ofsoftswitch13 +# oxm_hasmask always 0 +# oxm_length same as ofp_match etc (as without mask) +# linc/of_protocol +# oxm_hasmask always 0 +# oxm_length always 0 +# ovs: +# table-feature is not implemented +class OFPOxmId(StringifyMixin): + _PACK_STR = '!I' # oxm header + + _TYPE = { + 'ascii': [ + 'type', + ], + } + + def __init__(self, type_, hasmask=False, length=None): + self.type = type_ + self.hasmask = hasmask + self.length = length + # XXX experimenter + + @classmethod + def parse(cls, buf): + (oxm,) = struct.unpack_from(cls._PACK_STR, buffer(buf), 0) + (type_, _v) = ofproto_v1_3.oxm_to_user(oxm >> 9, None, None) + hasmask = ofproto_v1_3.oxm_tlv_header_extract_hasmask(oxm) + length = oxm & 0xff # XXX see the comment on OFPOxmId + rest = buf[4:] # XXX see the comment on OFPOxmId + return cls(type_=type_, hasmask=hasmask, length=length), rest + + def serialize(self): + # fixup + self.length = 0 # XXX see the comment on OFPOxmId + + (n, _v, _m) = ofproto_v1_3.oxm_from_user(self.type, None) + oxm = (n << 9) | (self.hasmask << 8) | self.length + buf = bytearray() + msg_pack_into(self._PACK_STR, buf, 0, oxm) + return buf + + +@OFPTableFeatureProp.register_type(ofproto_v1_3.OFPTFPT_MATCH) +@OFPTableFeatureProp.register_type(ofproto_v1_3.OFPTFPT_WILDCARDS) +@OFPTableFeatureProp.register_type(ofproto_v1_3.OFPTFPT_WRITE_SETFIELD) +@OFPTableFeatureProp.register_type(ofproto_v1_3.OFPTFPT_WRITE_SETFIELD_MISS) +@OFPTableFeatureProp.register_type(ofproto_v1_3.OFPTFPT_APPLY_SETFIELD) +@OFPTableFeatureProp.register_type(ofproto_v1_3.OFPTFPT_APPLY_SETFIELD_MISS) +class OFPTableFeaturePropOxm(OFPTableFeatureProp): + def __init__(self, type_, oxm_ids=[], length=None): + super(OFPTableFeaturePropOxm, self).__init__(type_, length) + self.oxm_ids = oxm_ids + + @classmethod + def _parse_prop(cls, buf): + rest = buf + ids = [] + while rest: + i, rest = OFPOxmId.parse(rest) + ids.append(i) + return { + 'oxm_ids': ids, + } + + def _serialize_prop(self): + bin_ids = bytearray() + for i in self.oxm_ids: + bin_ids += i.serialize() + return bin_ids + + +# XXX ofproto_v1_3.OFPTFPT_EXPERIMENTER +# XXX ofproto_v1_3.OFPTFPT_EXPERIMENTER_MISS + @_set_stats_type(ofproto_v1_3.OFPMP_TABLE_FEATURES, OFPTableFeaturesStats) @_set_msg_type(ofproto_v1_3.OFPT_MULTIPART_REQUEST) @@ -4751,21 +5046,17 @@ class OFPTableFeaturesStatsRequest(OFPMultipartRequest): This message is currently unimplemented. """ - def __init__(self, datapath, flags, length, table_id, name, - metadata_match, metadata_write, config, max_entries, - properties, type_=None): + def __init__(self, datapath, flags, + body=[], + properties=[], type_=None): super(OFPTableFeaturesStatsRequest, self).__init__(datapath, flags) - self.length = length - self.table_id = table_id - self.name = name - self.metadata_match = metadata_match - self.metadata_write = metadata_write - self.config = config - self.max_entries = max_entries + self.body = body def _serialize_stats_body(self): - # TODO - pass + bin_body = bytearray() + for p in self.body: + bin_body += p.serialize() + self.buf += bin_body @OFPMultipartReply.register_stats_type() |