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>
This commit is contained in:
parent
ce219b31b4
commit
c7503bfe0b
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
157
ryu/ofproto/oxs_fields.py
Normal file
157
ryu/ofproto/oxs_fields.py
Normal file
@ -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)
|
258
ryu/ofproto/oxx_fields.py
Normal file
258
ryu/ofproto/oxx_fields.py
Normal file
@ -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)
|
Loading…
Reference in New Issue
Block a user