27409a544e
Signed-off-by: Corey Bryant <corey.bryant@canonical.com> Reviewed-by: Minoru TAKAHASHI <takahashi.minoru7@gmail.com> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
263 lines
8.5 KiB
Python
263 lines
8.5 KiB
Python
# 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 six
|
|
import struct
|
|
|
|
from ryu.ofproto import ofproto_common
|
|
from ryu.lib.pack_utils import msg_pack_into
|
|
from ryu.lib import type_desc
|
|
|
|
if six.PY3:
|
|
_ord = int
|
|
else:
|
|
_ord = ord
|
|
|
|
# '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, six.integer_types):
|
|
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 = b''.join(six.int2byte(_ord(x) & _ord(y)) for (x, y) in zip(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 desc.exp_type == 2560:
|
|
# 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 oxx_type == exp_type | (OFPXXC_EXPERIMENTER << 7)
|
|
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)
|