oxm_fields.py 9.19 KB
Newer Older
Binh Nguyen's avatar
Binh Nguyen committed
1 2
# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2013 YAMAMOTO Takashi <yamamoto at valinux co jp>
root's avatar
root committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
#
# 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.

Binh Nguyen's avatar
Binh Nguyen committed
27 28 29 30
import itertools
import struct
import ofproto_common
from ofproto_parser import msg_pack_into
root's avatar
root committed
31 32 33 34

from ryu.lib import addrconv


Binh Nguyen's avatar
Binh Nguyen committed
35 36
class TypeDescr(object):
    pass
root's avatar
root committed
37 38


Binh Nguyen's avatar
Binh Nguyen committed
39 40 41 42 43 44 45 46 47 48 49
class IntDescr(TypeDescr):
    def __init__(self, size):
        self.size = size

    def to_user(self, bin):
        i = 0
        for x in xrange(self.size):
            c = bin[:1]
            i = i * 256 + ord(c)
            bin = bin[1:]
        return i
root's avatar
root committed
50

Binh Nguyen's avatar
Binh Nguyen committed
51 52 53 54 55 56
    def from_user(self, i):
        bin = ''
        for x in xrange(self.size):
            bin = chr(i & 255) + bin
            i /= 256
        return bin
root's avatar
root committed
57

Binh Nguyen's avatar
Binh Nguyen committed
58 59 60 61 62
Int1 = IntDescr(1)
Int2 = IntDescr(2)
Int3 = IntDescr(3)
Int4 = IntDescr(4)
Int8 = IntDescr(8)
root's avatar
root committed
63 64


Binh Nguyen's avatar
Binh Nguyen committed
65 66 67 68
class MacAddr(TypeDescr):
    size = 6
    to_user = addrconv.mac.bin_to_text
    from_user = addrconv.mac.text_to_bin
root's avatar
root committed
69 70


Binh Nguyen's avatar
Binh Nguyen committed
71 72 73 74
class IPv4Addr(TypeDescr):
    size = 4
    to_user = addrconv.ipv4.bin_to_text
    from_user = addrconv.ipv4.text_to_bin
root's avatar
root committed
75 76


Binh Nguyen's avatar
Binh Nguyen committed
77 78 79 80
class IPv6Addr(TypeDescr):
    size = 16
    to_user = addrconv.ipv6.bin_to_text
    from_user = addrconv.ipv6.text_to_bin
root's avatar
root committed
81 82


Binh Nguyen's avatar
Binh Nguyen committed
83 84
class UnknownType(TypeDescr):
    import base64
root's avatar
root committed
85

Binh Nguyen's avatar
Binh Nguyen committed
86 87
    to_user = staticmethod(base64.b64encode)
    from_user = staticmethod(base64.b64decode)
root's avatar
root committed
88 89


Binh Nguyen's avatar
Binh Nguyen committed
90
OFPXMC_OPENFLOW_BASIC = 0x8000
Binh Nguyen's avatar
Binh Nguyen committed
91
OFPXMC_NXM_1 = 0x0001
Binh Nguyen's avatar
Binh Nguyen committed
92
OFPXMC_EXPERIMENTER = 0xffff
root's avatar
root committed
93 94


Binh Nguyen's avatar
Binh Nguyen committed
95 96 97 98 99 100 101 102
class _OxmClass(object):
    def __init__(self, name, num, type_):
        self.name = name
        self.oxm_type = num | (self._class << 7)
        self.type = type_

class OpenFlowBasic(_OxmClass):
    _class = OFPXMC_OPENFLOW_BASIC
root's avatar
root committed
103

Binh Nguyen's avatar
Binh Nguyen committed
104 105 106
    def __init__(self, name, num, type_):
        super(OpenFlowBasic, self).__init__(name, num, type_)
        self.num = self.oxm_type
root's avatar
root committed
107

Binh Nguyen's avatar
Binh Nguyen committed
108 109 110 111 112 113 114 115 116 117 118 119 120
class NXM_1(_OxmClass):
    _class = OFPXMC_NXM_1 

    def __init__(self, name, num, type_):
        super(NXM_1, self).__init__(name, num, type_)
        self.num = self.oxm_type

class FlowBasic(_OxmClass):
    _class = OFPXMC_OPENFLOW_BASIC

    def __init__(self, name, num, type_):
        super(OpenFlowBasic, self).__init__(name, num, type_)
        self.num = self.oxm_type
root's avatar
root committed
121

Binh Nguyen's avatar
Binh Nguyen committed
122 123
class _Experimenter(_OxmClass):
    _class = OFPXMC_EXPERIMENTER
root's avatar
root committed
124 125


Binh Nguyen's avatar
Binh Nguyen committed
126 127
class ONFExperimenter(_Experimenter):
    experimenter_id = ofproto_common.ONF_EXPERIMENTER_ID
root's avatar
root committed
128

Binh Nguyen's avatar
Binh Nguyen committed
129 130 131 132
    def __init__(self, name, num, type_):
        super(ONFExperimenter, self).__init__(name, 0, type_)
        self.num = (ONFExperimenter, num)
        self.exp_type = num
root's avatar
root committed
133 134 135 136


def generate(modname):
    import sys
Binh Nguyen's avatar
Binh Nguyen committed
137
    import string
root's avatar
root committed
138 139 140 141 142 143 144 145
    import functools

    mod = sys.modules[modname]

    def add_attr(k, v):
        setattr(mod, k, v)

    for i in mod.oxm_types:
Binh Nguyen's avatar
Binh Nguyen committed
146
        uk = string.upper(i.name)
root's avatar
root committed
147 148
        if isinstance(i.num, tuple):
            continue
Binh Nguyen's avatar
Binh Nguyen committed
149 150
        oxm_class = i.num >> 7
        if oxm_class != OFPXMC_OPENFLOW_BASIC:
root's avatar
root committed
151
            continue
Binh Nguyen's avatar
Binh Nguyen committed
152
        ofpxmt = i.num & 0x3f
root's avatar
root committed
153 154 155 156 157 158 159
        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))

    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)
Binh Nguyen's avatar
Binh Nguyen committed
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
    add_attr('oxm_from_user', functools.partial(from_user, name_to_field))
    add_attr('oxm_to_user', functools.partial(to_user, 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_serialize', functools.partial(serialize, mod))
    add_attr('oxm_to_jsondict', to_jsondict)
    add_attr('oxm_from_jsondict', from_jsondict)


def from_user(name_to_field, name, user_value):
    try:
        f = name_to_field[name]
        t = f.type
        num = f.num
    except KeyError:
        t = UnknownType
        if name.startswith('field_'):
            num = int(name.split('_')[1])
        else:
            raise KeyError('unknown match 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
Binh Nguyen's avatar
Binh Nguyen committed
188
    if not value is None:
Binh Nguyen's avatar
Binh Nguyen committed
189
        value = t.from_user(value)
Binh Nguyen's avatar
Binh Nguyen committed
190
    if not mask is None:
Binh Nguyen's avatar
Binh Nguyen committed
191 192 193 194 195 196 197 198 199 200 201 202
        mask = t.from_user(mask)
    return num, value, mask


def to_user(num_to_field, n, v, m):
    try:
        f = num_to_field[n]
        t = f.type
        name = f.name
    except KeyError:
        t = UnknownType
        name = 'field_%d' % n
Binh Nguyen's avatar
Binh Nguyen committed
203
    if not v is None:
Binh Nguyen's avatar
Binh Nguyen committed
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
        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
Binh Nguyen's avatar
Binh Nguyen committed
225 226 227
    if not m is None:
        v = ''.join(chr(ord(x) & ord(y)) for (x, y)
            in itertools.izip(v, m))
Binh Nguyen's avatar
Binh Nguyen committed
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
    (k2, uv2) = mod.oxm_to_user(n, v, m)
    assert k2 == k
    return (k2, uv2)


def parse(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)
    len = mod.oxm_tlv_header_extract_length(header)
    oxm_class = oxm_type >> 7
    if oxm_class == OFPXMC_EXPERIMENTER:
        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)
        if exp_id == ofproto_common.ONF_EXPERIMENTER_ID:
            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)
            num = (ONFExperimenter, exp_type)
    else:
        num = oxm_type
        exp_hdr_len = 0
    value_offset = offset + hdr_len + exp_hdr_len
    value_len = len - exp_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 oxm_hasmask:
        (mask, ) = struct.unpack_from(value_pack_str, buf,
                                      value_offset + value_len)
    else:
        mask = None
    field_len = hdr_len + (header & 0xff)
    return num, value, mask, field_len


def serialize(mod, n, value, mask, buf, offset):
    exp_hdr = bytearray()
    if isinstance(n, tuple):
        (cls, exp_type) = n
        desc = mod._oxm_field_desc(n)
        assert issubclass(cls, _Experimenter)
        assert isinstance(desc, cls)
        assert cls is ONFExperimenter
        onf_exp_hdr_pack_str = '!IH'  # experimenter_id, exp_type
        msg_pack_into(onf_exp_hdr_pack_str, exp_hdr, 0,
                      cls.experimenter_id, exp_type)
        assert len(exp_hdr) == struct.calcsize(onf_exp_hdr_pack_str)
        n = desc.oxm_type
        assert (n >> 7) == OFPXMC_EXPERIMENTER
    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)

root's avatar
root committed
298

Binh Nguyen's avatar
Binh Nguyen committed
299
def to_jsondict(k, uv):
root's avatar
root committed
300 301 302 303 304 305 306 307
    if isinstance(uv, tuple):
        (value, mask) = uv
    else:
        value = uv
        mask = None
    return {"OXMTlv": {"field": k, "value": value, "mask": mask}}


Binh Nguyen's avatar
Binh Nguyen committed
308
def from_jsondict(j):
root's avatar
root committed
309 310 311 312 313 314 315 316 317
    tlv = j['OXMTlv']
    field = tlv['field']
    value = tlv['value']
    mask = tlv.get('mask')
    if mask is None:
        uv = value
    else:
        uv = (value, mask)
    return (field, uv)