Test Failed
Pull Request — master (#505)
by macartur
01:31
created

GenericTLV.header()   A

Complexity

Conditions 1

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1.125

Importance

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