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 <[email protected]>
---
 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 6499f32..80d85bb 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 f9a3668..62b91a2 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
 
@@ -449,12 +448,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 fbc96bf..e30f000 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 0000000..4169d3a
--- /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 0000000..8537b44
--- /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)
-- 
1.9.1



------------------------------------------------------------------------------
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to