Passed
Pull Request — master (#433)
by
unknown
02:40
created

ARP   A

Complexity

Total Complexity 5

Size/Duplication

Total Lines 92
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 5
c 1
b 0
f 0
dl 0
loc 92
ccs 37
cts 37
cp 1
rs 10

3 Methods

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