summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorYusuke Iwase <iwase.yusuke0@gmail.com>2015-05-28 11:50:37 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2015-06-18 18:24:05 +0900
commitc7503bfe0b22f40556a5a303fa44b8e8c8d71b61 (patch)
tree7187b8981c900d2a6e0332de3502d963a04f0700
parentce219b31b43a025e9a87173a16b11650ced59689 (diff)
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 <iwase.yusuke0@gmail.com> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r--ryu/lib/type_desc.py41
-rw-r--r--ryu/ofproto/ofproto_v1_5.py30
-rw-r--r--ryu/ofproto/oxm_fields.py260
-rw-r--r--ryu/ofproto/oxs_fields.py157
-rw-r--r--ryu/ofproto/oxx_fields.py258
5 files changed, 517 insertions, 229 deletions
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)