Passed
Push — master ( 928ac1...86913d )
by Beraldo
01:40 queued 10s
created

ListOfVLAN   A

Complexity

Total Complexity 1

Size/Duplication

Total Lines 14
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 1
c 1
b 0
f 0
dl 0
loc 14
ccs 3
cts 3
cp 1
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
A __init__() 0 8 1
1
"""Basic Network packet types.
2
3
Defines and Implements Basic Network packet types , such as Ethertnet and LLDP.
4
"""
5
6
# System imports
7 1
from copy import deepcopy
8 1
from enum import IntEnum
9
10
# Local source tree imports
11 1
from pyof.foundation.base import GenericStruct
12 1
from pyof.foundation.basic_types import (
13
    BinaryData, FixedTypeList, HWAddress, IPAddress, UBInt8, UBInt16)
14 1
from pyof.foundation.exceptions import PackException, UnpackException
15
16 1
__all__ = ('ARP', 'Ethernet', 'EtherType', 'GenericTLV', 'IPv4', 'VLAN',
17
           'TLVWithSubType', 'LLDP')
18
19
20
# NETWORK CONSTANTS AND ENUMS
21 1
class EtherType(IntEnum):
22
    """Enumeration with IEEE Ethernet types.
23
24
    The items are being added as we need.
25
    If you need one EtherType that is not listed below, please, send us a Pull
26
    Request with the addition.
27
28
    Ref: http://standards-oui.ieee.org/ethertype/eth.txt
29
30
    """
31
32
    #: Internet Protocol version 4 (IPv4)
33 1
    IPV4 = 0x0800
34
    #: Address Resolution Protocol (ARP)
35 1
    ARP = 0x0806
36
    #: Reverse Address Resolution Protocol
37 1
    RARP = 0x8035
38
    #: VLAN-tagged frame (IEEE 802.1Q) and Shortest Path Bridging IEEE 802.1aq
39
    #: with NNI compatibility[8]
40
    #: IEEE Std 802.1Q - Customer VLAN Tag Type
41 1
    VLAN = 0x8100
42
    #: Internet Protocol Version 6 (IPv6)
43 1
    IPV6 = 0x86DD
44
    #: Ethernet flow control
45 1
    ETHERNET_FLOW_CONTROL = 0x8808
46
    #: MPLS (multiprotocol label switching) label stack - unicast
47
    #: reference: RFC 3032 URL: ftp://ftp.rfc-editor.org/in-notes/rfc3032.txt
48 1
    MPLS_UNICAST = 0x8847
49
    #: MPLS (multiprotocol label switching) label stack - multicast
50
    #: reference: RFC 3032 URL: ftp://ftp.rfc-editor.org/in-notes/rfc3032.txt
51 1
    MPLS_MULTICAST = 0x8848
52
    #: Link Layer Discovery Protocol (LLDP)
53 1
    LLDP = 0x88CC
54
    #: VLAN-tagged (IEEE 802.1Q) frame with double tagging
55
    #: Std 802.1Q Service VLAN tag identifier
56 1
    VLAN_QINQ = 0x88a8
57
58
59 1
class ARP(GenericStruct):
60
    """ARP packet "struct".
61
62
    Contains fields for an ARP packet's header and data.
63
    Designed for Ethernet and IPv4 only: needs to have some attributes changed
64
    for other HTYPE and PTYPE implementations.
65
    Must be encapsulated inside an Ethernet frame.
66
    """
67
68 1
    htype = UBInt16()
69 1
    ptype = UBInt16()
70 1
    hlen = UBInt8()
71 1
    plen = UBInt8()
72 1
    oper = UBInt16()
73 1
    sha = HWAddress()
74 1
    spa = IPAddress()
75 1
    tha = HWAddress()
76 1
    tpa = IPAddress()
77
78 1
    def __init__(self, htype=1, ptype=EtherType.IPV4, hlen=6, plen=4, oper=1,
79
                 sha='00:00:00:00:00:00', spa='0.0.0.0',
80
                 tha="00:00:00:00:00:00", tpa='0.0.0.0'):
81
        """Create an ARP with the parameters below.
82
83
        Args:
84
            htype (int): Hardware protocol type. Defaults to 1 for Ethernet.
85
            ptype (int): Network protocol type. Defaults to 0x800 for IPv4.
86
            hlen (int): Length of the hardware address. Defaults to 6 for MAC
87
                        addresses.
88
            plen (int): Length of the networking protocol address. Defaults to
89
                        4 for IPv4 addresses.
90
            oper (int): Determines the operation for this ARP packet. Must be 1
91
                        for ARP request or 2 for ARP reply. Defaults to 1.
92
            sha (str): Sender hardware address. Defaults to
93
                       '00:00:00:00:00:00'.
94
            spa (str): Sender protocol address. Defaults to '0.0.0.0'.
95
            tha (str): Target hardware address. Defaults to
96
                       '00:00:00:00:00:00'.
97
            tpa (str): Target protocol address. Defaults to '0.0.0.0'.
98
        """
99 1
        super().__init__()
100 1
        self.htype = htype
101 1
        self.ptype = ptype
102 1
        self.hlen = hlen
103 1
        self.plen = plen
104 1
        self.oper = oper
105 1
        self.sha = sha
106 1
        self.spa = spa
107 1
        self.tha = tha
108 1
        self.tpa = tpa
109
110 1
    def is_valid(self):
111
        """Assure the ARP contains Ethernet and IPv4 information."""
112 1
        return self.htype == 1 and self.ptype == EtherType.IPV4
113
114 1
    def unpack(self, buff, offset=0):
115
        """Unpack a binary struct into this object's attributes.
116
117
        Return the values instead of the lib's basic types.
118
        Check if the protocols involved are Ethernet and IPv4. Other protocols
119
        are currently not supported.
120
121
        Args:
122
            buff (bytes): Binary buffer.
123
            offset (int): Where to begin unpacking.
124
125
        Raises:
126
            :exc:`~.exceptions.UnpackException`: If unpack fails.
127
128
        """
129 1
        super().unpack(buff, offset)
130 1
        if not self.is_valid():
131 1
            raise UnpackException("Unsupported protocols in ARP packet")
132
133
134 1
class VLAN(GenericStruct):
135
    """802.1q VLAN header."""
136
137
    #: tpid (:class:`UBInt16`): Tag Protocol Identifier
138 1
    tpid = UBInt16(EtherType.VLAN)
139
    #: _tci (:class:`UBInt16`): Tag Control Information - has the
140
    #: Priority Code Point, DEI/CFI bit and the VLAN ID
141 1
    _tci = UBInt16()
142
143 1
    def __init__(self, pcp=None, cfi=None, vid=None):
144
        """Create a VLAN with the parameters below.
145
146
        If no arguments are set for a particular instance, it is interpreted as
147
        abscence of VLAN information, and the pack() method will return an
148
        empty binary string.
149
150
        Args:
151
            tpid (int): Tag Protocol Identifier. Defaults to 0x8100 for 802.1q.
152
            pcp (int): 802.1p Priority Code Point. Defaults to 0 for Best
153
                       Effort Queue.
154
            cfi (int): Canonical Format Indicator. Defaults to 0 for Ethernet.
155
            vid (int): VLAN ID. If no VLAN is specified, value is 0.
156
        """
157 1
        super().__init__()
158 1
        self.tpid = EtherType.VLAN
159 1
        self.pcp = pcp
160 1
        self.cfi = cfi
161 1
        self.vid = vid
162
163 1
    def pack(self, value=None):
164
        """Pack the struct in a binary representation.
165
166
        Merge some fields to ensure correct packing.
167
168
        If no arguments are set for a particular instance, it is interpreted as
169
        abscence of VLAN information, and the pack() method will return an
170
        empty binary string.
171
172
        Returns:
173
            bytes: Binary representation of this instance.
174
175
        """
176 1
        if isinstance(value, type(self)):
177
            return value.pack()
178
179 1
        if self.pcp is None and self.cfi is None and self.vid is None:
180
            return b''
181 1
        self.pcp = self.pcp if self.pcp is not None else 0
182 1
        self.cfi = self.cfi if self.cfi is not None else 0
183 1
        self.vid = self.vid if self.vid is not None else 0
184 1
        self._tci = self.pcp << 13 | self.cfi << 12 | self.vid
185 1
        return super().pack()
186
187 1
    def _validate(self):
188
        """Assure this is a valid VLAN header instance."""
189 1
        if self.tpid.value not in (EtherType.VLAN, EtherType.VLAN_QINQ):
190 1
            raise UnpackException
191 1
        return
192
193 1
    def unpack(self, buff, offset=0):
194
        """Unpack a binary struct into this object's attributes.
195
196
        Return the values instead of the lib's basic types.
197
198
        After unpacking, the abscence of a `tpid` value causes the assignment
199
        of None to the field values to indicate that there is no VLAN
200
        information.
201
202
        Args:
203
            buff (bytes): Binary buffer.
204
            offset (int): Where to begin unpacking.
205
206
        Raises:
207
            :exc:`~.exceptions.UnpackException`: If unpack fails.
208
209
        """
210 1
        super().unpack(buff, offset)
211 1
        if self.tpid.value:
212 1
            self._validate()
213 1
            self.tpid = self.tpid.value
214 1
            self.pcp = self._tci.value >> 13
215 1
            self.cfi = (self._tci.value >> 12) & 1
216 1
            self.vid = self._tci.value & 4095
217
        else:
218
            self.tpid = EtherType.VLAN
219
            self.pcp = None
220
            self.cfi = None
221
            self.vid = None
222
223
224 1
class ListOfVLAN(FixedTypeList):
225
    """List of VLAN tags.
226
227
    Represented by instances of VLAN.
228
    """
229
230 1
    def __init__(self, items=None):
231
        """Create a ListOfVLAN with the optional parameters below.
232
233
        Args:
234
            items (:class:`~pyof.foundation.network_types.VLAN`):
235
                Instance or a list of instances.
236
        """
237 1
        super().__init__(pyof_class=VLAN, items=items)
238
239
240 1
class Ethernet(GenericStruct):
241
    """Ethernet "struct".
242
243
    Objects of this class represents an ethernet packet. It contains the
244
    'Ethernet header', composed by destination (MAC), source (MAC), type
245
    (EtherType)[1] and the payload of the packet, as binary data.
246
247
    This class does not consider the Ethernet 'Preamble' or the 'CRC'.
248
249
    There is also a get_hash method, that hashes the binary representation of
250
    the object so we can have a unique representation of the ethernet packet,
251
    so we can keep a track of ethernet packets being flooded over the network.
252
253
    [1] EtherTypes:
254
    http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml#ieee-802-numbers-1
255
    """
256
257 1
    destination = HWAddress()
258 1
    source = HWAddress()
259 1
    vlans = ListOfVLAN()
260 1
    ether_type = UBInt16()
261 1
    data = BinaryData()
262
263 1
    def __init__(self, destination=None, source=None, vlans=None,
264
                 ether_type=None, data=b''):
265
        """Create an instance and set its attributes.
266
267
        Args:
268
            destination (:class:`~pyof.foundation.basic_types.HWAddress`):
269
                The final destination MAC address.
270
            source (:class:`~pyof.foundation.basic_types.HWAddress`):
271
                The source Mac address of the packet.
272
            ether_type (:class:`~pyof.foundation.basic_types.UBInt16`):
273
                The EtherType of packet.
274
            data (:class:`~pyof.foundation.basic_types.BinaryData`):
275
                The content of the packet in binary format.
276
        """
277 1
        super().__init__()
278 1
        self.destination = destination
279 1
        self.source = source
280 1
        self.vlans = ListOfVLAN() if vlans is None else vlans
281 1
        self.ether_type = ether_type
282 1
        self.data = data
283
284 1
    def get_hash(self):
285
        """Calculate a hash and returns it.
286
287
        Returns:
288
            int: Integer value that identifies this instance.
289
290
        """
291
        return hash(self.pack())
292
293 1
    @staticmethod
294
    def _get_vlan_length(buff):
295
        """Return the total length of VLAN tags in a given Ethernet buffer."""
296 1
        length = 0
297 1
        begin = 12
298
299 1
        while(buff[begin:begin+2] in (EtherType.VLAN.to_bytes(2, 'big'),
300
                                      EtherType.VLAN_QINQ.to_bytes(2, 'big'))):
301 1
            length += 4
302 1
            begin += 4
303
304 1
        return length
305
306 1
    def unpack(self, buff, offset=0):
307
        """Unpack a binary message into this object's attributes.
308
309
        Unpack the binary value *buff* and update this object attributes based
310
        on the results.
311
312
        Ethernet headers may have VLAN tags. If no VLAN tag is found, a
313
        'wildcard VLAN tag' is inserted to assure correct unpacking.
314
315
        Args:
316
            buff (bytes): Binary data package to be unpacked.
317
            offset (int): Where to begin unpacking.
318
319
        Raises:
320
            UnpackException: If there is a struct unpacking error.
321
322
        """
323 1
        begin = offset
324
325 1
        vlan_length = self._get_vlan_length(buff)
326
327 1
        for attribute_name, class_attribute in self.get_class_attributes():
328 1
            attribute = deepcopy(class_attribute)
329 1
            if attribute_name == 'vlans':
330 1
                attribute.unpack(buff[begin:begin+vlan_length])
331
            else:
332 1
                attribute.unpack(buff, begin)
333 1
            setattr(self, attribute_name, attribute)
334 1
            begin += attribute.get_size()
335
336
337 1
class GenericTLV(GenericStruct):
338
    """TLV structure of LLDP packets.
339
340
    This is a Type, Length and Value (TLV) struct.
341
342
    The LLDP/TLV definition states that the Type field have 7 bits, while
343
    the length have 9 bits. The Value must be between 0-511 octets.
344
345
    Internally, on the instances of this class, the Type is a integer
346
    (0-127) and the Length is dynamically calculated based on the current
347
    type and value.
348
    """
349
350 1
    def __init__(self, tlv_type=127, value=None):
351
        """Create an instance and set its attributes.
352
353
        Args:
354
            tlv_type (int): Type used by this class. Defaults to 127.
355
            value (:class:`~pyof.foundation.basic_types.BinaryData`):
356
                Value stored by GenericTLV.
357
        """
358 1
        super().__init__()
359 1
        self.tlv_type = tlv_type
360 1
        self._value = BinaryData() if value is None else value
361
362 1
    @property
363
    def value(self):
364
        """Return the value stored by GenericTLV.
365
366
        Returns:
367
            :class:`~pyof.foundation.basic_types.BinaryData`:
368
                Value stored by GenericTLV.
369
370
        """
371 1
        return self._value
372
373 1
    @property
374
    def length(self):
375
        """Return the length of value stored by GenericTLV.
376
377
        Returns:
378
            int: Value length in bytes.
379
380
        """
381 1
        return len(self.value.pack())
382
383 1
    @property
384
    def header(self):
385
        """Header of the TLV Packet.
386
387
        The header is composed by the Type (7 bits) and Length (9 bits),
388
        summing up 16 bits. To achieve that, we need to do some bitshift
389
        operations.
390
391
        Returns:
392
            :class:`~pyof.foundation.basic_types.UBInt16`:
393
                Result after all operations.
394
395
        """
396 1
        return UBInt16(((self.tlv_type & 127) << 9) | (self.length & 511))
397
398 1
    def pack(self, value=None):
399
        """Pack the TLV in a binary representation.
400
401
        Returns:
402
            bytes: Binary representation of the struct object.
403
404
        Raises:
405
            :exc:`~.exceptions.ValidationError`: If validation fails.
406
407
        """
408 1
        if value is None:
409 1
            output = self.header.pack()
410 1
            output += self.value.pack()
411 1
            return output
412
413
        elif isinstance(value, type(self)):
414
            return value.pack()
415
        else:
416
            msg = "{} is not an instance of {}".format(value,
417
                                                       type(self).__name__)
418
            raise PackException(msg)
419
420 1
    def unpack(self, buff, offset=0):
421
        """Unpack a binary message into this object's attributes.
422
423
        Unpack the binary value *buff* and update this object attributes based
424
        on the results.
425
426
        Args:
427
            buff (bytes): Binary data package to be unpacked.
428
            offset (int): Where to begin unpacking.
429
430
        Raises:
431
            Exception: If there is a struct unpacking error.
432
433
        """
434 1
        header = UBInt16()
435 1
        header.unpack(buff[offset:offset+2])
436 1
        self.tlv_type = header.value >> 9
437 1
        length = header.value & 511
438 1
        begin, end = offset + 2, offset + 2 + length
439 1
        self._value = BinaryData(buff[begin:end])
440
441 1
    def get_size(self, value=None):
442
        """Return struct size.
443
444
        Returns:
445
            int: Returns the struct size based on inner attributes.
446
447
        """
448
        if isinstance(value, type(self)):
449
            return value.get_size()
450
451
        return 2 + self.length
452
453
454 1
class IPv4(GenericStruct):
455
    """IPv4 packet "struct".
456
457
    Contains all fields of an IP version 4 packet header, plus the upper layer
458
    content as binary data.
459
    Some of the fields were merged together because of their size being
460
    inferior to 8 bits. They are represented as a single class attribute, but
461
    pack/unpack methods will take into account the values in individual
462
    instance attributes.
463
    """
464
465
    #: _version_ihl (:class:`UBInt8`): IP protocol version + Internet Header
466
    #: Length (words)
467 1
    _version_ihl = UBInt8()
468
    #: _dscp_ecn (:class:`UBInt8`): Differentiated Services Code Point
469
    #: (ToS - Type of Service) + Explicit Congestion Notification
470 1
    _dscp_ecn = UBInt8()
471
    #: length (:class:`UBInt16`): IP packet length (bytes)
472 1
    length = UBInt16()
473
    #: identification (:class:`UBInt16`): Packet ID - common to all fragments
474 1
    identification = UBInt16()
475
    #: _flags_offset (:class:`UBInt16`): Fragmentation flags + fragmentation
476
    #: offset
477 1
    _flags_offset = UBInt16()
478
    #: ttl (:class:`UBInt8`): Packet time-to-live
479 1
    ttl = UBInt8()
480
    #: protocol (:class:`UBInt8`): Upper layer protocol number
481 1
    protocol = UBInt8()
482
    #: checksum (:class:`UBInt16`): Header checksum
483 1
    checksum = UBInt16()
484
    #: source (:class:`IPAddress`): Source IPv4 address
485 1
    source = IPAddress()
486
    #: destination (:class:`IPAddress`): Destination IPv4 address
487 1
    destination = IPAddress()
488
    #: options (:class:`BinaryData`): IP Options - up to 320 bits, always
489
    #: padded to 32 bits
490 1
    options = BinaryData()
491
    #: data (:class:`BinaryData`): Packet data
492 1
    data = BinaryData()
493
494 1
    def __init__(self, version=4, ihl=5, dscp=0, ecn=0, length=0,
495
                 identification=0, flags=0, offset=0, ttl=255, protocol=0,
496
                 checksum=0, source="0.0.0.0", destination="0.0.0.0",
497
                 options=b'', data=b''):
498
        """Create an IPv4 with the parameters below.
499
500
        Args:
501
            version (int): IP protocol version. Defaults to 4.
502
            ihl (int): Internet Header Length. Default is 5.
503
            dscp (int): Differentiated Service Code Point. Defaults to 0.
504
            ecn (int): Explicit Congestion Notification. Defaults to 0.
505
            length (int): IP packet length in bytes. Defaults to 0.
506
            identification (int): Packet Id. Defaults to 0.
507
            flags (int): IPv4 Flags. Defults 0.
508
            offset (int): IPv4 offset. Defaults to 0.
509
            ttl (int): Packet time-to-live. Defaults to 255
510
            protocol (int): Upper layer protocol number. Defaults to 0.
511
            checksum (int): Header checksum. Defaults to 0.
512
            source (str): Source IPv4 address. Defaults to "0.0.0.0"
513
            destination (str): Destination IPv4 address. Defaults to "0.0.0.0"
514
            options (bytes): IP options. Defaults to empty bytes.
515
            data (bytes): Packet data. Defaults to empty bytes.
516
        """
517 1
        super().__init__()
518 1
        self.version = version
519 1
        self.ihl = ihl
520 1
        self.dscp = dscp
521 1
        self.ecn = ecn
522 1
        self.length = length
523 1
        self.identification = identification
524 1
        self.flags = flags
525 1
        self.offset = offset
526 1
        self.ttl = ttl
527 1
        self.protocol = protocol
528 1
        self.checksum = checksum
529 1
        self.source = source
530 1
        self.destination = destination
531 1
        self.options = options
532 1
        self.data = data
533
534 1
    def _update_checksum(self):
535
        """Update the packet checksum to enable integrity check."""
536 1
        source_list = [int(octet) for octet in self.source.split(".")]
537 1
        destination_list = [int(octet) for octet in
538
                            self.destination.split(".")]
539 1
        source_upper = (source_list[0] << 8) + source_list[1]
540 1
        source_lower = (source_list[2] << 8) + source_list[3]
541 1
        destination_upper = (destination_list[0] << 8) + destination_list[1]
542 1
        destination_lower = (destination_list[2] << 8) + destination_list[3]
543
544 1
        block_sum = ((self._version_ihl << 8 | self._dscp_ecn) + self.length +
545
                     self.identification + self._flags_offset +
546
                     (self.ttl << 8 | self.protocol) + source_upper +
547
                     source_lower + destination_upper + destination_lower)
548
549 1
        while block_sum > 65535:
550 1
            carry = block_sum >> 16
551 1
            block_sum = (block_sum & 65535) + carry
552
553 1
        self.checksum = ~block_sum & 65535
554
555 1
    def pack(self, value=None):
0 ignored issues
show
Unused Code introduced by
The argument value seems to be unused.
Loading history...
556
        """Pack the struct in a binary representation.
557
558
        Merge some fields to ensure correct packing.
559
560
        Returns:
561
            bytes: Binary representation of this instance.
562
563
        """
564
        # Set the correct IHL based on options size
565 1
        if self.options:
566 1
            self.ihl += int(len(self.options) / 4)
567
568
        # Set the correct packet length based on header length and data
569 1
        self.length = int(self.ihl * 4 + len(self.data))
570
571 1
        self._version_ihl = self.version << 4 | self.ihl
572 1
        self._dscp_ecn = self.dscp << 2 | self.ecn
573 1
        self._flags_offset = self.flags << 13 | self.offset
574
575
        # Set the checksum field before packing
576 1
        self._update_checksum()
577
578 1
        return super().pack()
579
580 1
    def unpack(self, buff, offset=0):
581
        """Unpack a binary struct into this object's attributes.
582
583
        Return the values instead of the lib's basic types.
584
585
        Args:
586
            buff (bytes): Binary buffer.
587
            offset (int): Where to begin unpacking.
588
589
        Raises:
590
            :exc:`~.exceptions.UnpackException`: If unpack fails.
591
592
        """
593 1
        super().unpack(buff, offset)
594
595 1
        self.version = self._version_ihl.value >> 4
596 1
        self.ihl = self._version_ihl.value & 15
597 1
        self.dscp = self._dscp_ecn.value >> 2
598 1
        self.ecn = self._dscp_ecn.value & 3
599 1
        self.length = self.length.value
600 1
        self.identification = self.identification.value
601 1
        self.flags = self._flags_offset.value >> 13
602 1
        self.offset = self._flags_offset.value & 8191
603 1
        self.ttl = self.ttl.value
604 1
        self.protocol = self.protocol.value
605 1
        self.checksum = self.checksum.value
606 1
        self.source = self.source.value
607 1
        self.destination = self.destination.value
608
609 1
        if self.ihl > 5:
610 1
            options_size = (self.ihl - 5) * 4
611 1
            self.data = self.options.value[options_size:]
612 1
            self.options = self.options.value[:options_size]
613
        else:
614
            self.data = self.options.value
615
            self.options = b''
616
617
618 1
class TLVWithSubType(GenericTLV):
619
    """Modify the :class:`GenericTLV` to a Organization Specific TLV structure.
620
621
    Beyond the standard TLV (type, length, value), we can also have a more
622
    specific structure, with the :attr:`value` field being splitted into a
623
    :attr:`sub_type` field and a new :attr:`sub_value` field.
624
    """
625
626 1
    def __init__(self, tlv_type=1, sub_type=7, sub_value=None):
627
        """Create an instance and set its attributes.
628
629
        Args:
630
            tlv_type (int): Type used by this class. Defaults to 1.
631
            sub_type (int): Sub type value used by this class. Defaults to 7.
632
            sub_value (:class:`~pyof.foundation.basic_types.BinaryData`):
633
                Data stored by TLVWithSubType. Defaults to empty BinaryData.
634
        """
635 1
        super().__init__(tlv_type)
636 1
        self.sub_type = sub_type
637 1
        self.sub_value = BinaryData() if sub_value is None else sub_value
638
639 1
    @property
640
    def value(self):
641
        """Return sub type and sub value as binary data.
642
643
        Returns:
644
            :class:`~pyof.foundation.basic_types.BinaryData`:
645
                BinaryData calculated.
646
647
        """
648
        binary = UBInt8(self.sub_type).pack() + self.sub_value.pack()
649
        return BinaryData(binary)
650
651 1
    def unpack(self, buff, offset=0):
652
        """Unpack a binary message into this object's attributes.
653
654
        Unpack the binary value *buff* and update this object attributes based
655
        on the results.
656
657
        Args:
658
            buff (bytes): Binary data package to be unpacked.
659
            offset (int): Where to begin unpacking.
660
661
        Raises:
662
            Exception: If there is a struct unpacking error.
663
664
        """
665
        header = UBInt16()
666
        header.unpack(buff[offset:offset+2])
667
        self.tlv_type = header.value >> 9
668
        length = header.value & 511
669
        begin, end = offset + 2, offset + 2 + length
670
        sub_type = UBInt8()
671
        sub_type.unpack(buff[begin:begin+1])
672
        self.sub_type = sub_type.value
673
        self.sub_value = BinaryData(buff[begin+1:end])
674
675
676 1
class LLDP(GenericStruct):
677
    """LLDP class.
678
679
    Build a LLDP packet with TLVSubtype and Generic Subtypes.
680
681
    It contains a chassis_id TLV, a port_id TLV, a TTL (Time to live) and
682
    another TLV to represent the end of the LLDP Packet.
683
    """
684
685
    #: chassis_id (:class:`~TLVWithSubType`) with tlv_type = 1 and sub_type = 7
686 1
    chassis_id = TLVWithSubType(tlv_type=1, sub_type=7)
687
    #: port_id (:class:`TLVWithSubType`) with tlv = 2 and sub_type = 7
688 1
    port_id = TLVWithSubType(tlv_type=2, sub_type=7)
689
    #: TTL (:class:`GenericTLV`) time is given in seconds, between 0 and 65535,
690
    #: with tlv_type = 3
691 1
    ttl = GenericTLV(tlv_type=3, value=UBInt16(120))
692
    # We are not using list of tlvs for now
693
    # tlvs = ListOfTLVs()
694
    #: end (:class:`GenericTLV`) with tlv_type = 0
695
    end = GenericTLV(tlv_type=0)
696