From c7503bfe0b22f40556a5a303fa44b8e8c8d71b61 Mon Sep 17 00:00:00 2001 From: Yusuke Iwase Date: Thu, 28 May 2015 11:50:37 +0900 Subject: [PATCH] oxs_fields: Add OXS class support This patch adds OXS class parser for Extensible Flow Entry Statistics in OpenFlow Spec 1.5. And this implementation introduces oxx_fields.py to share the logic functions with oxm_fields.py. note: This pacth has no implementation for specific OXS Experimenter classes as oxm_fields.py has. (eg. ONF Experimenter, Nicira Experimenter) Signed-off-by: IWASE Yusuke Signed-off-by: FUJITA Tomonori --- ryu/lib/type_desc.py | 41 ++++++ ryu/ofproto/ofproto_v1_5.py | 30 +++-- ryu/ofproto/oxm_fields.py | 260 ++++++------------------------------ ryu/ofproto/oxs_fields.py | 157 ++++++++++++++++++++++ ryu/ofproto/oxx_fields.py | 258 +++++++++++++++++++++++++++++++++++ 5 files changed, 517 insertions(+), 229 deletions(-) create mode 100644 ryu/ofproto/oxs_fields.py create mode 100644 ryu/ofproto/oxx_fields.py diff --git a/ryu/lib/type_desc.py b/ryu/lib/type_desc.py index 6499f321..80d85bb2 100644 --- a/ryu/lib/type_desc.py +++ b/ryu/lib/type_desc.py @@ -48,6 +48,47 @@ Int4 = IntDescr(4) Int8 = IntDescr(8) +def _split_str(s, n): + """ + split string into list of strings by specified number. + """ + length = len(s) + return [s[i:i + n] for i in range(0, length, n)] + + +class IntDescrMlt(TypeDescr): + def __init__(self, length, num): + self.length = length + self.num = num + self.size = length * num + + def to_user(self, bin): + assert len(bin) == self.size + lb = _split_str(bin, self.length) + li = [] + for b in lb: + i = 0 + for x in range(self.length): + c = b[:1] + i = i * 256 + ord(c) + b = b[1:] + li.append(i) + return tuple(li) + + def from_user(self, li): + assert len(li) == self.num + bin = '' + for i in li: + b = '' + for x in range(self.length): + b = chr(i & 255) + b + i //= 256 + bin += b + return bin + +Int4Double = IntDescrMlt(4, 2) + + class MacAddr(TypeDescr): size = 6 to_user = addrconv.mac.bin_to_text diff --git a/ryu/ofproto/ofproto_v1_5.py b/ryu/ofproto/ofproto_v1_5.py index 6043a2ee..013de336 100644 --- a/ryu/ofproto/ofproto_v1_5.py +++ b/ryu/ofproto/ofproto_v1_5.py @@ -20,8 +20,7 @@ OpenFlow 1.5 definitions. from ryu.lib import type_desc from ryu.ofproto import oxm_fields -# TODO: oxs_fields -# from ryu.ofproto import oxs_fields +from ryu.ofproto import oxs_fields from struct import calcsize @@ -450,12 +449,27 @@ assert calcsize(OFP_STATS_PACK_STR) == OFP_STATS_SIZE OFPXSC_OPENFLOW_BASIC = 0x8002 # Basic stats class for OpenFlow OFPXSC_EXPERIMENTER = 0xFFFF # Experimenter class -# enum oxs_ofb_stat_fields -OFPXST_OFB_DURATION = 0 # Time flow entry has been alive. -OFPXST_OFB_IDLE_TIME = 1 # Time flow entry has been idle. -OFPXST_OFB_FLOW_COUNT = 3 # Number of aggregated flow entries. -OFPXST_OFB_PACKET_COUNT = 4 # Number of packets in flow entry. -OFPXST_OFB_BYTE_COUNT = 5 # Number of bytes in flow entry. + +def _oxs_tlv_header(class_, field, reserved, length): + return (class_ << 16) | (field << 9) | (reserved << 8) | length + + +def oxs_tlv_header(field, length): + return _oxs_tlv_header(OFPXSC_OPENFLOW_BASIC, field, 0, length) + + +def oxs_tlv_header_extract_length(header): + return header & 0xff + +oxs_types = [ + oxs_fields.OpenFlowBasic('duration', 0, type_desc.Int4Double), + oxs_fields.OpenFlowBasic('idle_time', 1, type_desc.Int4Double), + oxs_fields.OpenFlowBasic('flow_count', 3, type_desc.Int4), + oxs_fields.OpenFlowBasic('packet_count', 4, type_desc.Int8), + oxs_fields.OpenFlowBasic('byte_count', 5, type_desc.Int8), +] + +oxs_fields.generate(__name__) # enum ofp_action_type OFPAT_OUTPUT = 0 # Output to switch port. diff --git a/ryu/ofproto/oxm_fields.py b/ryu/ofproto/oxm_fields.py index fbc96bf8..e30f0005 100644 --- a/ryu/ofproto/oxm_fields.py +++ b/ryu/ofproto/oxm_fields.py @@ -60,11 +60,18 @@ # | reserved, should be zero | pbb_uca | # +-------------------------------+---------------+ -import itertools -import struct +from ryu.ofproto.oxx_fields import ( + _from_user, + _from_user_header, + _to_user, + _to_user_header, + _field_desc, + _normalize_user, + _parse, + _parse_header, + _serialize, + _serialize_header) from ryu.ofproto import ofproto_common -from ryu.lib.pack_utils import msg_pack_into -from ryu.lib import type_desc OFPXMC_NXM_0 = 0 # Nicira Extended Match (NXM_OF_) @@ -77,6 +84,7 @@ OFPXMC_EXPERIMENTER = 0xffff class _OxmClass(object): def __init__(self, name, num, type_): self.name = name + self.oxm_field = num self.oxm_type = num | (self._class << 7) # TODO(yamamoto): Clean this up later. # Probably when we drop EXT-256 style experimenter OXMs. @@ -146,239 +154,49 @@ def generate(modname): setattr(mod, k, v) for i in mod.oxm_types: - uk = i.name.upper() if isinstance(i.num, tuple): continue - oxm_class = i.num >> 7 - if oxm_class != OFPXMC_OPENFLOW_BASIC: + if i._class != OFPXMC_OPENFLOW_BASIC: continue - ofpxmt = i.num & 0x3f + uk = i.name.upper() + ofpxmt = i.oxm_field td = i.type add_attr('OFPXMT_OFB_' + uk, ofpxmt) add_attr('OXM_OF_' + uk, mod.oxm_tlv_header(ofpxmt, td.size)) add_attr('OXM_OF_' + uk + '_W', mod.oxm_tlv_header_w(ofpxmt, td.size)) + # 'oxx' indicates the OpenFlow Extensible class type. + # eg.) 'oxm' indicates that this class is OXM class. + oxx = 'oxm' name_to_field = dict((f.name, f) for f in mod.oxm_types) num_to_field = dict((f.num, f) for f in mod.oxm_types) - add_attr('oxm_from_user', functools.partial(_from_user, name_to_field)) + + # create functions by using oxx_fields module. + add_attr('oxm_from_user', + functools.partial(_from_user, oxx, name_to_field)) add_attr('oxm_from_user_header', - functools.partial(_from_user_header, name_to_field)) - add_attr('oxm_to_user', functools.partial(_to_user, num_to_field)) + functools.partial(_from_user_header, oxx, name_to_field)) + add_attr('oxm_to_user', + functools.partial(_to_user, oxx, num_to_field)) add_attr('oxm_to_user_header', - functools.partial(_to_user_header, num_to_field)) - add_attr('_oxm_field_desc', functools.partial(_field_desc, num_to_field)) - add_attr('oxm_normalize_user', functools.partial(_normalize_user, mod)) - add_attr('oxm_parse', functools.partial(_parse, mod)) - add_attr('oxm_parse_header', functools.partial(_parse_header, mod)) - add_attr('oxm_serialize', functools.partial(_serialize, mod)) - add_attr('oxm_serialize_header', functools.partial(_serialize_header, mod)) + functools.partial(_to_user_header, oxx, num_to_field)) + add_attr('_oxm_field_desc', # oxx is not required + functools.partial(_field_desc, num_to_field)) + add_attr('oxm_normalize_user', + functools.partial(_normalize_user, oxx, mod)) + add_attr('oxm_parse', # oxx is not required + functools.partial(_parse, mod)) + add_attr('oxm_parse_header', # oxx is not required + functools.partial(_parse_header, mod)) + add_attr('oxm_serialize', + functools.partial(_serialize, oxx, mod)) + add_attr('oxm_serialize_header', + functools.partial(_serialize_header, oxx, mod)) + add_attr('oxm_to_jsondict', _to_jsondict) add_attr('oxm_from_jsondict', _from_jsondict) -def _get_field_info_by_name(name_to_field, name): - try: - f = name_to_field[name] - t = f.type - num = f.num - except KeyError: - t = type_desc.UnknownType - if name.startswith('field_'): - num = int(name.split('_')[1]) - else: - raise KeyError('unknown match field ' + name) - return num, t - - -def _from_user_header(name_to_field, name): - (num, t) = _get_field_info_by_name(name_to_field, name) - return num - - -def _from_user(name_to_field, name, user_value): - (num, t) = _get_field_info_by_name(name_to_field, name) - # the 'list' case below is a bit hack; json.dumps silently maps - # python tuples into json lists. - if isinstance(user_value, (tuple, list)): - (value, mask) = user_value - else: - value = user_value - mask = None - if value is not None: - value = t.from_user(value) - if mask is not None: - mask = t.from_user(mask) - return num, value, mask - - -def _get_field_info_by_number(num_to_field, n): - try: - f = num_to_field[n] - t = f.type - name = f.name - except KeyError: - t = type_desc.UnknownType - name = 'field_%d' % (n,) - return name, t - - -def _to_user_header(num_to_field, n): - (name, t) = _get_field_info_by_number(num_to_field, n) - return name - - -def _to_user(num_to_field, n, v, m): - (name, t) = _get_field_info_by_number(num_to_field, n) - if v is not None: - if hasattr(t, 'size') and t.size != len(v): - raise Exception( - 'Unexpected OXM payload length %d for %s (expected %d)' - % (len(v), name, t.size)) - value = t.to_user(v) - else: - value = None - if m is None: - user_value = value - else: - user_value = (value, t.to_user(m)) - return name, user_value - - -def _field_desc(num_to_field, n): - return num_to_field[n] - - -def _normalize_user(mod, k, uv): - (n, v, m) = mod.oxm_from_user(k, uv) - # apply mask - if m is not None: - v = ''.join(chr(ord(x) & ord(y)) for (x, y) in itertools.izip(v, m)) - (k2, uv2) = mod.oxm_to_user(n, v, m) - assert k2 == k - return (k2, uv2) - - -def _parse_header_impl(mod, buf, offset): - hdr_pack_str = '!I' - (header, ) = struct.unpack_from(hdr_pack_str, buf, offset) - hdr_len = struct.calcsize(hdr_pack_str) - oxm_type = header >> 9 # class|field - oxm_hasmask = mod.oxm_tlv_header_extract_hasmask(header) - oxm_class = oxm_type >> 7 - oxm_length = header & 0xff - if oxm_class == OFPXMC_EXPERIMENTER: - # Experimenter OXMs have 64-bit header. (vs 32-bit for other OXMs) - exp_hdr_pack_str = '!I' # experimenter_id - (exp_id, ) = struct.unpack_from(exp_hdr_pack_str, buf, - offset + hdr_len) - exp_hdr_len = struct.calcsize(exp_hdr_pack_str) - assert exp_hdr_len == 4 - oxm_field = oxm_type & 0x7f - if exp_id == ofproto_common.ONF_EXPERIMENTER_ID and oxm_field == 0: - # XXX - # This block implements EXT-256 style experimenter OXM. - onf_exp_type_pack_str = '!H' - (exp_type, ) = struct.unpack_from(onf_exp_type_pack_str, buf, - offset + hdr_len + exp_hdr_len) - exp_hdr_len += struct.calcsize(onf_exp_type_pack_str) - assert exp_hdr_len == 4 + 2 - num = (exp_id, exp_type) - else: - num = (exp_id, oxm_type) - else: - num = oxm_type - exp_hdr_len = 0 - value_len = oxm_length - exp_hdr_len - if oxm_hasmask: - value_len //= 2 - assert value_len > 0 - field_len = hdr_len + oxm_length - total_hdr_len = hdr_len + exp_hdr_len - return num, total_hdr_len, oxm_hasmask, value_len, field_len - - -def _parse_header(mod, buf, offset): - (oxm_type_num, total_hdr_len, hasmask, value_len, - field_len) = _parse_header_impl(mod, buf, offset) - return oxm_type_num, field_len - value_len - - -def _parse(mod, buf, offset): - (oxm_type_num, total_hdr_len, hasmask, value_len, - field_len) = _parse_header_impl(mod, buf, offset) - # Note: OXM payload length (oxm_len) includes Experimenter ID (exp_hdr_len) - # for experimenter OXMs. - value_offset = offset + total_hdr_len - value_pack_str = '!%ds' % value_len - assert struct.calcsize(value_pack_str) == value_len - (value, ) = struct.unpack_from(value_pack_str, buf, value_offset) - if hasmask: - (mask, ) = struct.unpack_from(value_pack_str, buf, - value_offset + value_len) - else: - mask = None - return oxm_type_num, value, mask, field_len - - -def _make_exp_hdr(mod, n): - exp_hdr = bytearray() - try: - desc = mod._oxm_field_desc(n) - except KeyError: - return n, exp_hdr - if isinstance(desc, _Experimenter): # XXX - (exp_id, exp_type) = n - assert desc.experimenter_id == exp_id - if isinstance(desc, OldONFExperimenter): # XXX - # XXX - # This block implements EXT-256 style experimenter OXM. - exp_hdr_pack_str = '!IH' # experimenter_id, exp_type - msg_pack_into(exp_hdr_pack_str, exp_hdr, 0, - desc.experimenter_id, desc.exp_type) - else: - assert desc.oxm_type == exp_type - exp_hdr_pack_str = '!I' # experimenter_id - msg_pack_into(exp_hdr_pack_str, exp_hdr, 0, - desc.experimenter_id) - assert len(exp_hdr) == struct.calcsize(exp_hdr_pack_str) - n = desc.oxm_type - assert (n >> 7) == OFPXMC_EXPERIMENTER - return n, exp_hdr - - -def _serialize_header(mod, n, buf, offset): - try: - desc = mod._oxm_field_desc(n) - value_len = desc.type.size - except KeyError: - value_len = 0 - n, exp_hdr = _make_exp_hdr(mod, n) - exp_hdr_len = len(exp_hdr) - pack_str = "!I%ds" % (exp_hdr_len,) - msg_pack_into(pack_str, buf, offset, - (n << 9) | (0 << 8) | (exp_hdr_len + value_len), - bytes(exp_hdr)) - return struct.calcsize(pack_str) - - -def _serialize(mod, n, value, mask, buf, offset): - n, exp_hdr = _make_exp_hdr(mod, n) - exp_hdr_len = len(exp_hdr) - value_len = len(value) - if mask: - assert value_len == len(mask) - pack_str = "!I%ds%ds%ds" % (exp_hdr_len, value_len, len(mask)) - msg_pack_into(pack_str, buf, offset, - (n << 9) | (1 << 8) | (exp_hdr_len + value_len * 2), - bytes(exp_hdr), value, mask) - else: - pack_str = "!I%ds%ds" % (exp_hdr_len, value_len,) - msg_pack_into(pack_str, buf, offset, - (n << 9) | (0 << 8) | (exp_hdr_len + value_len), - bytes(exp_hdr), value) - return struct.calcsize(pack_str) - - def _to_jsondict(k, uv): if isinstance(uv, tuple): (value, mask) = uv diff --git a/ryu/ofproto/oxs_fields.py b/ryu/ofproto/oxs_fields.py new file mode 100644 index 00000000..4169d3ad --- /dev/null +++ b/ryu/ofproto/oxs_fields.py @@ -0,0 +1,157 @@ +# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation. +# +# 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. + +# there are two representations of value which this module deal with. +# +# "user" +# the readable value which are strings. +# +# "internal" +# the on-wire bytes value. + +# There are two types of OXS headers. +# +# 32-bit OXS header +# 31 16 15 9 8 7 0 +# +-------------------------------+-------------+-+---------------+ +# | class | field |r| length | +# +-------------------------------+-------------+-+---------------+ +# +# 64-bit experimenter OXS header +# 31 16 15 9 8 7 0 +# +-------------------------------+-------------+-+---------------+ +# | class (OFPXSC_EXPERIMENTER) | field |r| length | +# +-------------------------------+-------------+-+---------------+ +# | experimenter ID | +# +---------------------------------------------------------------+ +# +# Description of OXS header fields +# +----------------------+-------+--------------------------------------------+ +# | Name | Width | Usage | +# +----------+-----------+-------+--------------------------------------------+ +# | oxs_type | oxs_class | 16 | Stat class: member class or reserved class | +# | +-----------+-------+--------------------------------------------+ +# | | oxs_field | 7 | Stat field within the class | +# +----------+-----------+-------+--------------------------------------------+ +# | reserved | 1 | Reserved for future use | +# +----------------------+-------+--------------------------------------------+ +# | length | 8 | Length of OXS payload | +# +----------------------+-------+--------------------------------------------+ + +from ryu.ofproto.oxx_fields import ( + _from_user, + _from_user_header, + _to_user, + _to_user_header, + _field_desc, + _parse, + _parse_header, + _serialize, + _serialize_header) + + +OFPXSC_OPENFLOW_BASIC = 0x8002 +OFPXSC_EXPERIMENTER = 0xFFFF + + +OFPXSC_HEADER_PACK_STR = '!I' +OFPXSC_EXP_HEADER_PACK_STR = '!I' + + +class _OxsClass(object): + # _class = OFPXSC_* must be an attribute of subclass. + def __init__(self, name, num, type_): + self.name = name + self.oxs_field = num + self.oxs_type = num | (self._class << 7) + # 'num' has not corresponding field in the specification. + # This is specific to this implementation and used to retrieve + # _OxsClass subclass from 'num_to_field' dictionary. + self.num = self.oxs_type + self.type = type_ + + +class OpenFlowBasic(_OxsClass): + _class = OFPXSC_OPENFLOW_BASIC + + +class _Experimenter(_OxsClass): + _class = OFPXSC_EXPERIMENTER + # experimenter_id must be an attribute of subclass. + + def __init__(self, name, num, type_): + super(_Experimenter, self).__init__(name, num, type_) + self.num = (self.experimenter_id, self.oxs_type) + + +def generate(modname): + import sys + import functools + + mod = sys.modules[modname] + + def add_attr(k, v): + setattr(mod, k, v) + + for i in mod.oxs_types: + if isinstance(i.num, tuple): + continue + if i._class != OFPXSC_OPENFLOW_BASIC: + continue + uk = i.name.upper() + ofpxst = i.oxs_field + td = i.type + add_attr('OFPXST_OFB_' + uk, ofpxst) + add_attr('OXS_OF_' + uk, mod.oxs_tlv_header(ofpxst, td.size)) + + # 'oxx' indicates the OpenFlow Extensible class type. + # eg.) 'oxs' indicates that this class is OXS class. + oxx = 'oxs' + name_to_field = dict((f.name, f) for f in mod.oxs_types) + num_to_field = dict((f.num, f) for f in mod.oxs_types) + + # create functions by using oxx_fields module. + add_attr('oxs_from_user', + functools.partial(_from_user, oxx, name_to_field)) + add_attr('oxs_from_user_header', + functools.partial(_from_user_header, oxx, name_to_field)) + add_attr('oxs_to_user', + functools.partial(_to_user, oxx, num_to_field)) + add_attr('oxs_to_user_header', + functools.partial(_to_user_header, oxx, num_to_field)) + add_attr('_oxs_field_desc', # oxx is not required + functools.partial(_field_desc, num_to_field)) + add_attr('oxs_parse', # oxx is not required + functools.partial(_parse, mod)) + add_attr('oxs_parse_header', # oxx is not required + functools.partial(_parse_header, mod)) + add_attr('oxs_serialize', + functools.partial(_serialize, oxx, mod)) + add_attr('oxs_serialize_header', + functools.partial(_serialize_header, oxx, mod)) + + add_attr('oxs_to_jsondict', _to_jsondict) + add_attr('oxs_from_jsondict', _from_jsondict) + + +def _to_jsondict(k, uv): + return {"OXSTlv": {"field": k, "value": uv}} + + +def _from_jsondict(j): + tlv = j['OXSTlv'] + field = tlv['field'] + value = tlv['value'] + return (field, value) diff --git a/ryu/ofproto/oxx_fields.py b/ryu/ofproto/oxx_fields.py new file mode 100644 index 00000000..8537b44f --- /dev/null +++ b/ryu/ofproto/oxx_fields.py @@ -0,0 +1,258 @@ +# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation. +# +# 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. + +# there are two representations of value and mask this module deal with. +# +# "user" +# (value, mask) or value. the latter means no mask. +# value and mask are strings. +# +# "internal" +# value and mask are on-wire bytes. +# mask is None if no mask. + +import itertools +import struct + +from ryu.ofproto import ofproto_common +from ryu.lib.pack_utils import msg_pack_into +from ryu.lib import type_desc + +# 'OFPXXC_EXPERIMENTER' has not corresponding field in the specification. +# This is transparently value for Experimenter class ID for OXM/OXS. +OFPXXC_EXPERIMENTER = 0xffff + + +def _get_field_info_by_name(oxx, name_to_field, name): + try: + f = name_to_field[name] + t = f.type + num = f.num + except KeyError: + t = type_desc.UnknownType + if name.startswith('field_'): + num = int(name.split('_')[1]) + else: + raise KeyError('unknown %s field: %s' % (oxx.upper(), name)) + return num, t + + +def _from_user_header(oxx, name_to_field, name): + (num, t) = _get_field_info_by_name(oxx, name_to_field, name) + return num + + +def _from_user(oxx, name_to_field, name, user_value): + (num, t) = _get_field_info_by_name(oxx, name_to_field, name) + # the 'list' case below is a bit hack; json.dumps silently maps + # python tuples into json lists. + if oxx == 'oxm' and isinstance(user_value, (tuple, list)): + (value, mask) = user_value + else: + value = user_value + mask = None + if value is not None: + value = t.from_user(value) + if mask is not None: + mask = t.from_user(mask) + return num, value, mask + + +def _get_field_info_by_number(oxx, num_to_field, n): + try: + f = num_to_field[n] + t = f.type + name = f.name + except KeyError: + t = type_desc.UnknownType + if isinstance(n, int): + name = 'field_%d' % (n,) + else: + raise KeyError('unknown %s field number: %s' % (oxx.upper(), n)) + return name, t + + +def _to_user_header(oxx, num_to_field, n): + (name, t) = _get_field_info_by_number(oxx, num_to_field, n) + return name + + +def _to_user(oxx, num_to_field, n, v, m): + (name, t) = _get_field_info_by_number(oxx, num_to_field, n) + if v is not None: + if isinstance(v, (tuple, list)): + v_len = len(v) * len(v[0]) + else: + v_len = len(v) + if hasattr(t, 'size') and t.size != v_len: + raise Exception( + 'Unexpected %s payload length %d for %s (expected %d)' + % (oxx.upper(), v_len, name, t.size)) + value = t.to_user(v) + else: + value = None + if m is None: + user_value = value + else: + user_value = (value, t.to_user(m)) + return name, user_value + + +def _field_desc(num_to_field, n): + return num_to_field[n] + + +def _normalize_user(oxx, mod, k, uv): + try: + from_user = getattr(mod, oxx + '_from_user') + (n, v, m) = from_user(k, uv) + except: + return (k, uv) + # apply mask + if m is not None: + v = ''.join(chr(ord(x) & ord(y)) for (x, y) in itertools.izip(v, m)) + try: + to_user = getattr(mod, oxx + '_to_user') + (k2, uv2) = to_user(n, v, m) + except: + return (k, uv) + assert k2 == k + return (k2, uv2) + + +def _parse_header_impl(mod, buf, offset): + hdr_pack_str = '!I' + (header, ) = struct.unpack_from(hdr_pack_str, buf, offset) + hdr_len = struct.calcsize(hdr_pack_str) + oxx_type = header >> 9 # class|field + oxm_hasmask = mod.oxm_tlv_header_extract_hasmask(header) + oxx_class = oxx_type >> 7 + oxx_length = header & 0xff + if oxx_class == OFPXXC_EXPERIMENTER: + # Experimenter OXMs/OXSs have 64-bit header. + # (vs 32-bit for other OXMs/OXSs) + exp_hdr_pack_str = '!I' # experimenter_id + (exp_id, ) = struct.unpack_from(exp_hdr_pack_str, buf, + offset + hdr_len) + exp_hdr_len = struct.calcsize(exp_hdr_pack_str) + assert exp_hdr_len == 4 + oxx_field = oxx_type & 0x7f + if exp_id == ofproto_common.ONF_EXPERIMENTER_ID and oxx_field == 0: + # XXX + # This block implements EXT-256 style experimenter OXM. + onf_exp_type_pack_str = '!H' + (exp_type, ) = struct.unpack_from(onf_exp_type_pack_str, buf, + offset + hdr_len + exp_hdr_len) + exp_hdr_len += struct.calcsize(onf_exp_type_pack_str) + assert exp_hdr_len == 4 + 2 + num = (exp_id, exp_type) + else: + num = (exp_id, oxx_type) + else: + num = oxx_type + exp_hdr_len = 0 + value_len = oxx_length - exp_hdr_len + if oxm_hasmask: + value_len //= 2 + assert value_len > 0 + field_len = hdr_len + oxx_length + total_hdr_len = hdr_len + exp_hdr_len + return num, total_hdr_len, oxm_hasmask, value_len, field_len + + +def _parse_header(mod, buf, offset): + (oxx_type_num, total_hdr_len, hasmask, value_len, + field_len) = _parse_header_impl(mod, buf, offset) + return oxx_type_num, field_len - value_len + + +def _parse(mod, buf, offset): + (oxx_type_num, total_hdr_len, hasmask, value_len, + field_len) = _parse_header_impl(mod, buf, offset) + # Note: OXM/OXS payload length (oxx_len) includes Experimenter ID + # (exp_hdr_len) for experimenter OXMs/OXSs. + value_offset = offset + total_hdr_len + value_pack_str = '!%ds' % value_len + assert struct.calcsize(value_pack_str) == value_len + (value, ) = struct.unpack_from(value_pack_str, buf, value_offset) + if hasmask: + (mask, ) = struct.unpack_from(value_pack_str, buf, + value_offset + value_len) + else: + mask = None + return oxx_type_num, value, mask, field_len + + +def _make_exp_hdr(oxx, mod, n): + exp_hdr = bytearray() + try: + get_desc = getattr(mod, '_' + oxx + '_field_desc') + desc = get_desc(n) + except KeyError: + return n, exp_hdr + if desc._class == OFPXXC_EXPERIMENTER: + (exp_id, exp_type) = n + assert desc.experimenter_id == exp_id + oxx_type = getattr(desc, oxx + '_type') + if hasattr(desc, 'exp_type'): # XXX + # XXX + # This block implements EXT-256 style experimenter OXM. + assert desc.exp_type == 2560 + exp_hdr_pack_str = '!IH' # experimenter_id, exp_type + msg_pack_into(exp_hdr_pack_str, exp_hdr, 0, + desc.experimenter_id, desc.exp_type) + else: + assert oxx_type == exp_type + exp_hdr_pack_str = '!I' # experimenter_id + msg_pack_into(exp_hdr_pack_str, exp_hdr, 0, + desc.experimenter_id) + assert len(exp_hdr) == struct.calcsize(exp_hdr_pack_str) + n = oxx_type + assert (n >> 7) == OFPXXC_EXPERIMENTER + return n, exp_hdr + + +def _serialize_header(oxx, mod, n, buf, offset): + try: + get_desc = getattr(mod, '_' + oxx + '_field_desc') + desc = get_desc(n) + value_len = desc.type.size + except KeyError: + value_len = 0 + n, exp_hdr = _make_exp_hdr(oxx, mod, n) + exp_hdr_len = len(exp_hdr) + pack_str = "!I%ds" % (exp_hdr_len,) + msg_pack_into(pack_str, buf, offset, + (n << 9) | (0 << 8) | (exp_hdr_len + value_len), + bytes(exp_hdr)) + return struct.calcsize(pack_str) + + +def _serialize(oxx, mod, n, value, mask, buf, offset): + n, exp_hdr = _make_exp_hdr(oxx, mod, n) + exp_hdr_len = len(exp_hdr) + value_len = len(value) + if mask: + assert value_len == len(mask) + pack_str = "!I%ds%ds%ds" % (exp_hdr_len, value_len, len(mask)) + msg_pack_into(pack_str, buf, offset, + (n << 9) | (1 << 8) | (exp_hdr_len + value_len * 2), + bytes(exp_hdr), value, mask) + else: + pack_str = "!I%ds%ds" % (exp_hdr_len, value_len,) + msg_pack_into(pack_str, buf, offset, + (n << 9) | (0 << 8) | (exp_hdr_len + value_len), + bytes(exp_hdr), value) + return struct.calcsize(pack_str)