Passed
Push — master ( 75d007...4d9626 )
by Humberto
01:10 queued 12s
created

build.utils.emit_message_in()   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 3
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
"""of_core utility functions and classes."""
2
3 1
import struct
4 1
from collections import OrderedDict
5
6 1
from pyof.foundation.exceptions import PackException, UnpackException
7 1
from pyof.v0x01.common.header import Type as OFPTYPE
8
9 1
from kytos.core import KytosEvent
10
11
12 1
def of_slicer(remaining_data):
13
    """Slice a raw `bytes` instance into OpenFlow packets."""
14 1
    data_len = len(remaining_data)
15 1
    pkts = []
16 1
    while data_len > 3:
17 1
        length_field = struct.unpack('!H', remaining_data[2:4])[0]
18 1
        if data_len >= length_field:
19 1
            pkts.append(remaining_data[:length_field])
20 1
            remaining_data = remaining_data[length_field:]
21 1
            data_len = len(remaining_data)
22
        else:
23
            break
24 1
    return pkts, remaining_data
25
26
27 1
def _unpack_int(packet, offset=0, size=None):
28 1
    if size is None:
29
        if isinstance(packet, int):
30
            return packet
31
        size = len(packet)
32 1
    return int.from_bytes(packet[offset:offset + size], byteorder='big')
33
34
35 1
def _emit_message(controller, connection, message, direction):
36
    """Emit a KytosEvent for every incoming or outgoing message."""
37 1
    if direction == 'in':
38 1
        address_type = 'source'
39 1
        message_buffer = controller.buffers.msg_in
40 1
    elif direction == 'out':
41 1
        address_type = 'destination'
42 1
        message_buffer = controller.buffers.msg_out
43
    else:
44
        raise Exception("direction must be 'in' or 'out'")
45
46 1
    name = message.header.message_type.name.lower()
47 1
    hex_version = 'v0x%0.2x' % (message.header.version + 0)
48 1
    of_event = KytosEvent(
49
        name=f"kytos/of_core.{hex_version}.messages.{direction}.{name}",
50
        content={'message': message,
51
                 address_type: connection})
52 1
    message_buffer.put(of_event)
53
54
55 1
def emit_message_in(controller, connection, message):
56
    """Emit a KytosEvent for every incoming message."""
57 1
    _emit_message(controller, connection, message, 'in')
58
59
60 1
def emit_message_out(controller, connection, message):
61
    """Emit a KytosEvent for every outgoing message."""
62 1
    _emit_message(controller, connection, message, 'out')
63
64
65 1
class GenericHello:
66
    """Version agnostic OpenFlow Hello Message."""
67
68 1
    header_sizes = OrderedDict(
69
        version=1,
70
        type=1,
71
        length=2,
72
        xid=4)
73
74 1
    elem_type_size = 2
75 1
    elem_len_size = 2
76
77 1
    OFPHET_VERSIONBITMAP = 1
78
79 1
    class GenericHeader:
80
        """Generic header for the OpenFlow message."""
81
82 1
        xid = None
83 1
        type = None
84 1
        length = None
85
86 1
    def __init__(self, *, packet=None, versions=None, xid=None):
87
        """Initialize from a binary packet or from initial versions and xid.
88
89
        Parameters:
90
            packet: binary packet (bytes) to be unpacked and used to
91
                    initialize the message
92
            versions: list of versions used to build the version bitmap
93
            xid: xid to be used in the message
94
95
        """
96 1
        self.header = self.GenericHeader()
97 1
        self.header.message_type = OFPTYPE.OFPT_HELLO
98 1
        if not any((packet, versions)):
99
            raise Exception('either packet or versions must be set.')
100
101 1
        if packet is not None:
102 1
            self.unpack(packet)
103
        else:
104
            if xid is None:
105
                self.header.xid = 0
106
107 1
        if versions is not None:
108 1
            self.versions = versions
109 1
            self.header.version = max(versions)
110 1
        if xid is not None:
111
            self.header.xid = xid
112
113 1
    def pack(self):
114
        """Encode OpenFlow packet."""
115 1
        versions = self.versions
116 1
        xid = self.header.xid
117 1
        packet_version = max(versions)
118 1
        if packet_version > 31:
119
            raise PackException
120 1
        versions_value = 0
121 1
        for version in versions:
122 1
            versions_value += (1 << version)
123 1
        bitmap = versions_value.to_bytes(4, byteorder='big')
124 1
        version_byte = packet_version.to_bytes(self.header_sizes['version'],
125
                                               byteorder='big')
126 1
        xid_byte = xid.to_bytes(self.header_sizes['xid'],
127
                                byteorder='big')
128 1
        packet = version_byte + b'\x00\x00\x10' + xid_byte + \
129
            b'\x00\x01\x00\x08' + bitmap
130 1
        return packet
131
132 1
    def unpack(self, packet):
133
        """Decode OpenFlow packet."""
134 1
        offset = 0
135
        # self.header = self.generic_Header()
136 1
        for key, size in self.header_sizes.items():
137 1
            setattr(self.header, key, _unpack_int(packet, offset, size))
138 1
            offset += size
139
140 1
        if self.header.type != 0:
141
            raise UnpackException
142
143 1
        elements = {}
144 1
        try:
145 1
            while offset < self.header.length:
146 1
                elem_type = _unpack_int(
147
                    packet, offset, self.elem_type_size)
148
149 1
                offset += self.elem_type_size
150 1
                elem_length = _unpack_int(
151
                    packet, offset, self.elem_len_size)
152
153 1
                offset += self.elem_len_size
154 1
                elem_header_size = self.elem_type_size - self.elem_len_size
155 1
                elem_value_size = elem_length - elem_header_size
156 1
                elem_value = _unpack_int(
157
                    packet, offset, elem_value_size)
158
159 1
                elements[elem_type] = elem_value
160
        except IndexError:
161
            raise UnpackException
162
163 1
        self.elements = elements
164
165 1
        if self.OFPHET_VERSIONBITMAP in self.elements:
166 1
            bitmap = self.elements[self.OFPHET_VERSIONBITMAP]
167 1
            versions = []
168 1
            for i in range(32):
169 1
                if ((1 << i) & bitmap) != 0:
170 1
                    versions.append(i)
171 1
            self.versions = versions
172 1
            self.version_bitmap = bitmap
173
        else:
174 1
            self.versions = None
175
176
177 1
class NegotiationException(Exception):
178
    """Exception raised when OpenFlow version negotiation failed."""
179
180 1
    def __str__(self):
181
        return "OF version negotiation failed: " + super().__str__()
182