Home | History | Annotate | Download | only in contrib
      1 #! /usr/bin/env python
      2 #
      3 # scapy.contrib.description = LLDP
      4 # scapy.contrib.status = loads
      5 
      6 """
      7     LLDP - Link Layer Discovery Protocol
      8     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      9 
     10     :author:    Thomas Tannhaeuser, hecke (at] naberius.de
     11     :license:   GPLv2
     12 
     13         This module is free software; you can redistribute it and/or
     14         modify it under the terms of the GNU General Public License
     15         as published by the Free Software Foundation; either version 2
     16         of the License, or (at your option) any later version.
     17 
     18         This module is distributed in the hope that it will be useful,
     19         but WITHOUT ANY WARRANTY; without even the implied warranty of
     20         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     21         GNU General Public License for more details.
     22 
     23     :description:
     24 
     25         This module provides Scapy layers for the LLDP protocol.
     26 
     27         normative references:
     28             - IEEE 802.1AB 2016 - LLDP protocol, topology and MIB description
     29 
     30     :TODO:
     31         - organization specific TLV e.g. ProfiNet
     32 
     33     :NOTES:
     34         - you can find the layer configuration options at the end of this file
     35         - default configuration enforces standard conform
     36           - frame structure
     37                 (ChassisIDTLV/PortIDTLV/TimeToLiveTLV/.../EndofLLDPDUTLV)
     38           - multiplicity of TLVs (if given by the standard)
     39           - min sizes of strings used by the TLVs
     40         - conf.contribs['LLDP'].strict_mode_disable() -> disable strict mode
     41         - strict mode = True => conf.debug_dissector = True
     42 
     43 """
     44 from scapy.config import conf
     45 from scapy.layers.dot11 import Packet
     46 from scapy.layers.l2 import Ether, Dot1Q, bind_layers, \
     47     BitField, StrLenField, ByteEnumField, BitEnumField, \
     48     BitFieldLenField, ShortField, Padding, Scapy_Exception, \
     49     XStrLenField
     50 from scapy.modules.six.moves import range
     51 from scapy.data import ETHER_TYPES
     52 from scapy.compat import orb, raw
     53 
     54 LLDP_NEAREST_BRIDGE_MAC = '01:80:c2:00:00:0e'
     55 LLDP_NEAREST_NON_TPMR_BRIDGE_MAC = '01:80:c2:00:00:03'
     56 LLDP_NEAREST_CUSTOMER_BRIDGE_MAC = '01:80:c2:00:00:00'
     57 
     58 LLDP_ETHER_TYPE = 0x88cc
     59 ETHER_TYPES['LLDP'] = LLDP_ETHER_TYPE
     60 
     61 
     62 class LLDPInvalidFrameStructure(Scapy_Exception):
     63     """
     64     basic frame structure not standard conform
     65     (missing TLV, invalid order or multiplicity)
     66     """
     67     pass
     68 
     69 
     70 class LLDPInvalidLastLayerException(Scapy_Exception):
     71     """
     72     in strict mode, last layer in frame must be of type LLDPDUEndOfLLDPDU
     73     """
     74     pass
     75 
     76 
     77 class LLDPMissingLowerLayer(Scapy_Exception):
     78     """
     79     first layer below first LLDPDU must be Ethernet or Dot1q
     80     """
     81     pass
     82 
     83 
     84 class LLDPInvalidTLVCount(Scapy_Exception):
     85     """
     86     invalid number of entries for a specific TLV type
     87     """
     88     pass
     89 
     90 
     91 class LLDPInvalidLengthField(Scapy_Exception):
     92     """
     93     invalid value of length field
     94     """
     95     pass
     96 
     97 
     98 class LLDPDU(Packet):
     99     """
    100     base class for all LLDP data units
    101     """
    102     TYPES = {
    103         0x00: 'end of LLDPDU',
    104         0x01: 'chassis id',
    105         0x02: 'port id',
    106         0x03: 'time to live',
    107         0x04: 'port description',
    108         0x05: 'system name',
    109         0x06: 'system description',
    110         0x07: 'system capabilities',
    111         0x08: 'management address',
    112         range(0x09, 0x7e): 'reserved - future standardization',
    113         127: 'organisation specific TLV'
    114     }
    115 
    116     DOT1Q_HEADER_LEN = 4
    117     ETHER_HEADER_LEN = 14
    118     ETHER_FSC_LEN = 4
    119     ETHER_FRAME_MIN_LEN = 64
    120 
    121     LAYER_STACK = []
    122     LAYER_MULTIPLICITIES = {}
    123 
    124     def guess_payload_class(self, payload):
    125         # type is a 7-bit bitfield spanning bits 1..7 -> div 2
    126         lldpdu_tlv_type = orb(payload[0]) // 2
    127         return LLDPDU_CLASS_TYPES[lldpdu_tlv_type]
    128 
    129     @staticmethod
    130     def _dot1q_headers_size(layer):
    131         """
    132         calculate size of lower dot1q layers (if present)
    133         :param layer: the layer to start at
    134         :return: size of vlan headers, layer below lowest vlan header
    135         """
    136 
    137         vlan_headers_size = 0
    138         under_layer = layer
    139 
    140         while under_layer and isinstance(under_layer, Dot1Q):
    141             vlan_headers_size += LLDPDU.DOT1Q_HEADER_LEN
    142             under_layer = under_layer.underlayer
    143 
    144         return vlan_headers_size, under_layer
    145 
    146     def post_build(self, pkt, pay):
    147 
    148         last_layer = not pay
    149         if last_layer and conf.contribs['LLDP'].strict_mode() and \
    150                         type(self).__name__ != LLDPDUEndOfLLDPDU.__name__:
    151             raise LLDPInvalidLastLayerException('Last layer must be instance '
    152                                                 'of LLDPDUEndOfLLDPDU - '
    153                                                 'got {}'.
    154                                                 format(type(self).__name__))
    155 
    156         under_layer = self.underlayer
    157 
    158         if under_layer is None:
    159             if conf.contribs['LLDP'].strict_mode():
    160                 raise LLDPMissingLowerLayer('No lower layer (Ethernet '
    161                                             'or Dot1Q) provided.')
    162             else:
    163                 return pkt + pay
    164 
    165         if isinstance(under_layer, LLDPDU):
    166             return pkt + pay
    167 
    168         frame_size, under_layer = LLDPDU._dot1q_headers_size(under_layer)
    169 
    170         if not under_layer or not isinstance(under_layer, Ether):
    171             if conf.contribs['LLDP'].strict_mode():
    172                 raise LLDPMissingLowerLayer('No Ethernet layer provided.')
    173             else:
    174                 return pkt + pay
    175 
    176         frame_size += LLDPDU.ETHER_HEADER_LEN
    177         frame_size += len(pkt) + len(pay) + LLDPDU.ETHER_FSC_LEN
    178         if frame_size < LLDPDU.ETHER_FRAME_MIN_LEN:
    179             return pkt + pay + b'\x00' * (LLDPDU.ETHER_FRAME_MIN_LEN - frame_size)
    180         return pkt + pay
    181 
    182     @staticmethod
    183     def _frame_structure_check(structure_description):
    184         """
    185         check if the structure of the frame is conform to the basic
    186         frame structure defined by the standard
    187         :param structure_description: string-list reflecting LLDP-msg structure
    188         """
    189 
    190         standard_frame_structure = [LLDPDUChassisID.__name__,
    191                                     LLDPDUPortID.__name__,
    192                                     LLDPDUTimeToLive.__name__,
    193                                     '<...>',
    194                                     LLDPDUEndOfLLDPDU.__name__]
    195 
    196         if len(structure_description) < 4:
    197             raise LLDPInvalidFrameStructure(
    198                 'Invalid frame structure.\ngot: {}\nexpected: '
    199                 '{}'.format(' '.join(structure_description),
    200                             ' '.join(standard_frame_structure)))
    201 
    202         for idx, layer_name in enumerate(standard_frame_structure):
    203 
    204             if layer_name == '<...>':
    205                 break
    206             if layer_name != structure_description[idx]:
    207                 raise LLDPInvalidFrameStructure(
    208                     'Invalid frame structure.\ngot: {}\nexpected: '
    209                     '{}'.format(' '.join(structure_description),
    210                                 ' '.join(standard_frame_structure)))
    211 
    212         if structure_description[-1] != standard_frame_structure[-1]:
    213             raise LLDPInvalidFrameStructure(
    214                 'Invalid frame structure.\ngot: {}\nexpected: '
    215                 '{}'.format(' '.join(structure_description),
    216                             ' '.join(standard_frame_structure)))
    217 
    218     @staticmethod
    219     def _tlv_multiplicities_check(tlv_type_count):
    220         """
    221         check if multiplicity of present TLVs conforms to the standard
    222         :param tlv_type_count: dict containing counte-per-TLV
    223         """
    224 
    225         # * : 0..n, 1 : one and only one.
    226         standard_multiplicities = {
    227             LLDPDUEndOfLLDPDU.__name__: 1,
    228             LLDPDUChassisID.__name__: 1,
    229             LLDPDUPortID.__name__: 1,
    230             LLDPDUTimeToLive.__name__: 1,
    231             LLDPDUPortDescription: '*',
    232             LLDPDUSystemName: '*',
    233             LLDPDUSystemDescription: '*',
    234             LLDPDUSystemCapabilities: '*',
    235             LLDPDUManagementAddress: '*'
    236         }
    237 
    238         for tlv_type_name in standard_multiplicities:
    239 
    240             standard_tlv_multiplicity = \
    241                 standard_multiplicities[tlv_type_name]
    242             if standard_tlv_multiplicity == '*':
    243                 continue
    244 
    245             try:
    246                 if tlv_type_count[tlv_type_name] != standard_tlv_multiplicity:
    247                     raise LLDPInvalidTLVCount(
    248                         'Invalid number of entries for TLV type '
    249                         '{} - expected {} entries, got '
    250                         '{}'.format(tlv_type_name,
    251                                     standard_tlv_multiplicity,
    252                                     tlv_type_count[tlv_type_name]))
    253 
    254             except KeyError:
    255                 raise LLDPInvalidTLVCount('Missing TLV layer of type '
    256                                           '{}.'.format(tlv_type_name))
    257 
    258     def pre_dissect(self, s):
    259 
    260         if conf.contribs['LLDP'].strict_mode():
    261             if self.__class__.__name__ == 'LLDPDU':
    262                 LLDPDU.LAYER_STACK = []
    263                 LLDPDU.LAYER_MULTIPLICITIES = {}
    264             else:
    265                 LLDPDU.LAYER_STACK.append(self.__class__.__name__)
    266                 try:
    267                     LLDPDU.LAYER_MULTIPLICITIES[self.__class__.__name__] += 1
    268                 except KeyError:
    269                     LLDPDU.LAYER_MULTIPLICITIES[self.__class__.__name__] = 1
    270 
    271         return s
    272 
    273     def dissection_done(self, pkt):
    274 
    275         if self.__class__.__name__ == 'LLDPDU' and \
    276                 conf.contribs['LLDP'].strict_mode():
    277             LLDPDU._frame_structure_check(LLDPDU.LAYER_STACK)
    278             LLDPDU._tlv_multiplicities_check(LLDPDU.LAYER_MULTIPLICITIES)
    279 
    280         super(LLDPDU, self).dissection_done(pkt)
    281 
    282 
    283 class LLDPDUChassisID(LLDPDU):
    284     """
    285         ieee 802.1ab-2016 - sec. 8.5.2 / p. 26
    286     """
    287     LLDP_CHASSIS_ID_TLV_SUBTYPES = {
    288         0x00: 'reserved',
    289         0x01: 'chassis component',
    290         0x02: 'interface alias',
    291         0x03: 'port component',
    292         0x04: 'MAC address',
    293         0x05: 'network address',
    294         0x06: 'interface name',
    295         0x07: 'locally assigned',
    296         range(0x08, 0xff): 'reserved'
    297     }
    298 
    299     SUBTYPE_RESERVED = 0x00
    300     SUBTYPE_CHASSIS_COMPONENT = 0x01
    301     SUBTYPE_INTERFACE_ALIAS = 0x02
    302     SUBTYPE_PORT_COMPONENT = 0x03
    303     SUBTYPE_MAC_ADDRESS = 0x04
    304     SUBTYPE_NETWORK_ADDRESS = 0x05
    305     SUBTYPE_INTERFACE_NAME = 0x06
    306     SUBTYPE_LOCALLY_ASSIGNED = 0x07
    307 
    308     fields_desc = [
    309         BitEnumField('_type', 0x01, 7, LLDPDU.TYPES),
    310         BitFieldLenField('_length', None, 9, length_of='id',
    311                          adjust=lambda pkt, x: len(pkt.id) + 1),
    312         ByteEnumField('subtype', 0x00, LLDP_CHASSIS_ID_TLV_SUBTYPES),
    313         XStrLenField('id', '', length_from=lambda pkt: pkt._length - 1)
    314     ]
    315 
    316     def _check(self):
    317         """
    318         run layer specific checks
    319         """
    320         if conf.contribs['LLDP'].strict_mode() and not self.id:
    321             raise LLDPInvalidLengthField('id must be >= 1 characters long')
    322 
    323     def post_dissect(self, s):
    324         self._check()
    325         return super(LLDPDUChassisID, self).post_dissect(s)
    326 
    327     def do_build(self):
    328         self._check()
    329         return super(LLDPDUChassisID, self).do_build()
    330 
    331 
    332 class LLDPDUPortID(LLDPDU):
    333     """
    334         ieee 802.1ab-2016 - sec. 8.5.3 / p. 26
    335     """
    336     LLDP_PORT_ID_TLV_SUBTYPES = {
    337         0x00: 'reserved',
    338         0x01: 'interface alias',
    339         0x02: 'port component',
    340         0x03: 'MAC address',
    341         0x04: 'network address',
    342         0x05: 'interface name',
    343         0x06: 'agent circuit ID',
    344         0x07: 'locally assigned',
    345         range(0x08, 0xff): 'reserved'
    346     }
    347 
    348     SUBTYPE_RESERVED = 0x00
    349     SUBTYPE_INTERFACE_ALIAS = 0x01
    350     SUBTYPE_PORT_COMPONENT = 0x02
    351     SUBTYPE_MAC_ADDRESS = 0x03
    352     SUBTYPE_NETWORK_ADDRESS = 0x04
    353     SUBTYPE_INTERFACE_NAME = 0x05
    354     SUBTYPE_AGENT_CIRCUIT_ID = 0x06
    355     SUBTYPE_LOCALLY_ASSIGNED = 0x07
    356 
    357     fields_desc = [
    358         BitEnumField('_type', 0x02, 7, LLDPDU.TYPES),
    359         BitFieldLenField('_length', None, 9, length_of='id',
    360                          adjust=lambda pkt, x: len(pkt.id) + 1),
    361         ByteEnumField('subtype', 0x00, LLDP_PORT_ID_TLV_SUBTYPES),
    362         StrLenField('id', '', length_from=lambda pkt: pkt._length - 1)
    363     ]
    364 
    365     def _check(self):
    366         """
    367         run layer specific checks
    368         """
    369         if conf.contribs['LLDP'].strict_mode() and not self.id:
    370             raise LLDPInvalidLengthField('id must be >= 1 characters long')
    371 
    372     def post_dissect(self, s):
    373         self._check()
    374         return super(LLDPDUPortID, self).post_dissect(s)
    375 
    376     def do_build(self):
    377         self._check()
    378         return super(LLDPDUPortID, self).do_build()
    379 
    380 
    381 class LLDPDUTimeToLive(LLDPDU):
    382     """
    383         ieee 802.1ab-2016 - sec. 8.5.4 / p. 29
    384     """
    385     fields_desc = [
    386         BitEnumField('_type', 0x03, 7, LLDPDU.TYPES),
    387         BitField('_length', 0x02, 9),
    388         ShortField('ttl', 20)
    389     ]
    390 
    391     def _check(self):
    392         """
    393         run layer specific checks
    394         """
    395         if conf.contribs['LLDP'].strict_mode() and self._length != 2:
    396             raise LLDPInvalidLengthField('length must be 2 - got '
    397                                          '{}'.format(self._length))
    398 
    399     def post_dissect(self, s):
    400         self._check()
    401         return super(LLDPDUTimeToLive, self).post_dissect(s)
    402 
    403     def do_build(self):
    404         self._check()
    405         return super(LLDPDUTimeToLive, self).do_build()
    406 
    407 
    408 class LLDPDUEndOfLLDPDU(LLDPDU):
    409     """
    410         ieee 802.1ab-2016 - sec. 8.5.1 / p. 26
    411     """
    412     fields_desc = [
    413         BitEnumField('_type', 0x00, 7, LLDPDU.TYPES),
    414         BitField('_length', 0x00, 9),
    415     ]
    416 
    417     def extract_padding(self, s):
    418         return '', s
    419 
    420     def _check(self):
    421         """
    422         run layer specific checks
    423         """
    424         if conf.contribs['LLDP'].strict_mode() and self._length != 0:
    425             raise LLDPInvalidLengthField('length must be 0 - got '
    426                                          '{}'.format(self._length))
    427 
    428     def post_dissect(self, s):
    429         self._check()
    430         return super(LLDPDUEndOfLLDPDU, self).post_dissect(s)
    431 
    432     def do_build(self):
    433         self._check()
    434         return super(LLDPDUEndOfLLDPDU, self).do_build()
    435 
    436 
    437 class LLDPDUPortDescription(LLDPDU):
    438     """
    439         ieee 802.1ab-2016 - sec. 8.5.5 / p. 29
    440     """
    441     fields_desc = [
    442         BitEnumField('_type', 0x04, 7, LLDPDU.TYPES),
    443         BitFieldLenField('_length', None, 9, length_of='description'),
    444         StrLenField('description', '', length_from=lambda pkt: pkt._length)
    445     ]
    446 
    447 
    448 class LLDPDUSystemName(LLDPDU):
    449     """
    450         ieee 802.1ab-2016 - sec. 8.5.6 / p. 30
    451     """
    452     fields_desc = [
    453         BitEnumField('_type', 0x05, 7, LLDPDU.TYPES),
    454         BitFieldLenField('_length', None, 9, length_of='system_name'),
    455         StrLenField('system_name', '', length_from=lambda pkt: pkt._length)
    456     ]
    457 
    458 
    459 class LLDPDUSystemDescription(LLDPDU):
    460     """
    461         ieee 802.1ab-2016 - sec. 8.5.7 / p. 31
    462     """
    463     fields_desc = [
    464         BitEnumField('_type', 0x06, 7, LLDPDU.TYPES),
    465         BitFieldLenField('_length', None, 9, length_of='description'),
    466         StrLenField('description', '', length_from=lambda pkt: pkt._length)
    467     ]
    468 
    469 
    470 class LLDPDUSystemCapabilities(LLDPDU):
    471     """
    472         ieee 802.1ab-2016 - sec. 8.5.8 / p. 31
    473     """
    474     fields_desc = [
    475         BitEnumField('_type', 0x07, 7, LLDPDU.TYPES),
    476         BitFieldLenField('_length', 4, 9),
    477         BitField('reserved_5_available', 0, 1),
    478         BitField('reserved_4_available', 0, 1),
    479         BitField('reserved_3_available', 0, 1),
    480         BitField('reserved_2_available', 0, 1),
    481         BitField('reserved_1_available', 0, 1),
    482         BitField('two_port_mac_relay_available', 0, 1),
    483         BitField('s_vlan_component_available', 0, 1),
    484         BitField('c_vlan_component_available', 0, 1),
    485         BitField('station_only_available', 0, 1),
    486         BitField('docsis_cable_device_available', 0, 1),
    487         BitField('telephone_available', 0, 1),
    488         BitField('router_available', 0, 1),
    489         BitField('wlan_access_point_available', 0, 1),
    490         BitField('mac_bridge_available', 0, 1),
    491         BitField('repeater_available', 0, 1),
    492         BitField('other_available', 0, 1),
    493         BitField('reserved_5_enabled', 0, 1),
    494         BitField('reserved_4_enabled', 0, 1),
    495         BitField('reserved_3_enabled', 0, 1),
    496         BitField('reserved_2_enabled', 0, 1),
    497         BitField('reserved_1_enabled', 0, 1),
    498         BitField('two_port_mac_relay_enabled', 0, 1),
    499         BitField('s_vlan_component_enabled', 0, 1),
    500         BitField('c_vlan_component_enabled', 0, 1),
    501         BitField('station_only_enabled', 0, 1),
    502         BitField('docsis_cable_device_enabled', 0, 1),
    503         BitField('telephone_enabled', 0, 1),
    504         BitField('router_enabled', 0, 1),
    505         BitField('wlan_access_point_enabled', 0, 1),
    506         BitField('mac_bridge_enabled', 0, 1),
    507         BitField('repeater_enabled', 0, 1),
    508         BitField('other_enabled', 0, 1),
    509     ]
    510 
    511     def _check(self):
    512         """
    513         run layer specific checks
    514         """
    515         if conf.contribs['LLDP'].strict_mode() and self._length != 4:
    516             raise LLDPInvalidLengthField('length must be 4 - got '
    517                                          '{}'.format(self._length))
    518 
    519     def post_dissect(self, s):
    520         self._check()
    521         return super(LLDPDUSystemCapabilities, self).post_dissect(s)
    522 
    523     def do_build(self):
    524         self._check()
    525         return super(LLDPDUSystemCapabilities, self).do_build()
    526 
    527 
    528 class LLDPDUManagementAddress(LLDPDU):
    529     """
    530         ieee 802.1ab-2016 - sec. 8.5.9 / p. 32
    531 
    532         currently only 0x00..0x1e are used by standards, no way to
    533         use anything > 0xff as management address subtype is only
    534         one octet wide
    535 
    536         see https://www.iana.org/assignments/
    537         address-family-numbers/address-family-numbers.xhtml
    538     """
    539     IANA_ADDRESS_FAMILY_NUMBERS = {
    540         0x00: 'other',
    541         0x01: 'IPv4',
    542         0x02: 'IPv6',
    543         0x03: 'NSAP',
    544         0x04: 'HDLC',
    545         0x05: 'BBN',
    546         0x06: '802',
    547         0x07: 'E.163',
    548         0x08: 'E.164',
    549         0x09: 'F.69',
    550         0x0a: 'X.121',
    551         0x0b: 'IPX',
    552         0x0c: 'Appletalk',
    553         0x0d: 'Decnet IV',
    554         0x0e: 'Banyan Vines',
    555         0x0f: 'E.164 with NSAP',
    556         0x10: 'DNS',
    557         0x11: 'Distinguished Name',
    558         0x12: 'AS Number',
    559         0x13: 'XTP over IPv4',
    560         0x14: 'XTP over IPv6',
    561         0x15: 'XTP native mode XTP',
    562         0x16: 'Fiber Channel World-Wide Port Name',
    563         0x17: 'Fiber Channel World-Wide Node Name',
    564         0x18: 'GWID',
    565         0x19: 'AFI for L2VPN',
    566         0x1a: 'MPLS-TP Section Endpoint ID',
    567         0x1b: 'MPLS-TP LSP Endpoint ID',
    568         0x1c: 'MPLS-TP Pseudowire Endpoint ID',
    569         0x1d: 'MT IP Multi-Topology IPv4',
    570         0x1e: 'MT IP Multi-Topology IPv6'
    571     }
    572 
    573     SUBTYPE_MANAGEMENT_ADDRESS_OTHER = 0x00
    574     SUBTYPE_MANAGEMENT_ADDRESS_IPV4 = 0x01
    575     SUBTYPE_MANAGEMENT_ADDRESS_IPV6 = 0x02
    576     SUBTYPE_MANAGEMENT_ADDRESS_NSAP = 0x03
    577     SUBTYPE_MANAGEMENT_ADDRESS_HDLC = 0x04
    578     SUBTYPE_MANAGEMENT_ADDRESS_BBN = 0x05
    579     SUBTYPE_MANAGEMENT_ADDRESS_802 = 0x06
    580     SUBTYPE_MANAGEMENT_ADDRESS_E_163 = 0x07
    581     SUBTYPE_MANAGEMENT_ADDRESS_E_164 = 0x08
    582     SUBTYPE_MANAGEMENT_ADDRESS_F_69 = 0x09
    583     SUBTYPE_MANAGEMENT_ADDRESS_X_121 = 0x0A
    584     SUBTYPE_MANAGEMENT_ADDRESS_IPX = 0x0B
    585     SUBTYPE_MANAGEMENT_ADDRESS_APPLETALK = 0x0C
    586     SUBTYPE_MANAGEMENT_ADDRESS_DECNET_IV = 0x0D
    587     SUBTYPE_MANAGEMENT_ADDRESS_BANYAN_VINES = 0x0E
    588     SUBTYPE_MANAGEMENT_ADDRESS_E_164_WITH_NSAP = 0x0F
    589     SUBTYPE_MANAGEMENT_ADDRESS_DNS = 0x10
    590     SUBTYPE_MANAGEMENT_ADDRESS_DISTINGUISHED_NAME = 0x11
    591     SUBTYPE_MANAGEMENT_ADDRESS_AS_NUMBER = 0x12
    592     SUBTYPE_MANAGEMENT_ADDRESS_XTP_OVER_IPV4 = 0x13
    593     SUBTYPE_MANAGEMENT_ADDRESS_XTP_OVER_IPV6 = 0x14
    594     SUBTYPE_MANAGEMENT_ADDRESS_XTP_NATIVE_MODE_XTP = 0x15
    595     SUBTYPE_MANAGEMENT_ADDRESS_FIBER_CHANNEL_WORLD_WIDE_PORT_NAME = 0x16
    596     SUBTYPE_MANAGEMENT_ADDRESS_FIBER_CHANNEL_WORLD_WIDE_NODE_NAME = 0x17
    597     SUBTYPE_MANAGEMENT_ADDRESS_GWID = 0x18
    598     SUBTYPE_MANAGEMENT_ADDRESS_AFI_FOR_L2VPN = 0x19
    599     SUBTYPE_MANAGEMENT_ADDRESS_MPLS_TP_SECTION_ENDPOINT_ID = 0x1A
    600     SUBTYPE_MANAGEMENT_ADDRESS_MPLS_TP_LSP_ENDPOINT_ID = 0x1B
    601     SUBTYPE_MANAGEMENT_ADDRESS_MPLS_TP_PSEUDOWIRE_ENDPOINT_ID = 0x1C
    602     SUBTYPE_MANAGEMENT_ADDRESS_MT_IP_MULTI_TOPOLOGY_IPV4 = 0x1D
    603     SUBTYPE_MANAGEMENT_ADDRESS_MT_IP_MULTI_TOPOLOGY_IPV6 = 0x1E
    604 
    605     INTERFACE_NUMBERING_SUBTYPES = {
    606         0x01: 'unknown',
    607         0x02: 'ifIndex',
    608         0x03: 'system port number'
    609     }
    610 
    611     SUBTYPE_INTERFACE_NUMBER_UNKNOWN = 0x01
    612     SUBTYPE_INTERFACE_NUMBER_IF_INDEX = 0x02
    613     SUBTYPE_INTERFACE_NUMBER_SYSTEM_PORT_NUMBER = 0x03
    614 
    615     '''
    616         Note - calculation of _length field:
    617         _length = 1@_management_address_string_length +
    618                   1@management_address_subtype +
    619                   management_address.len +
    620                   1@interface_numbering_subtype +
    621                   4@interface_number +
    622                   1@_oid_string_length +
    623                   object_id.len
    624     '''
    625 
    626     fields_desc = [
    627         BitEnumField('_type', 0x08, 7, LLDPDU.TYPES),
    628         BitFieldLenField('_length', None, 9, length_of='management_address',
    629                          adjust=lambda pkt, x:
    630                          8 + len(pkt.management_address) + len(pkt.object_id)),
    631         BitFieldLenField('_management_address_string_length', None, 8,
    632                          length_of='management_address',
    633                          adjust=lambda pkt, x: len(pkt.management_address) + 1),
    634         ByteEnumField('management_address_subtype', 0x00,
    635                       IANA_ADDRESS_FAMILY_NUMBERS),
    636         XStrLenField('management_address', '',
    637                      length_from=lambda pkt:
    638                      pkt._management_address_string_length - 1),
    639         ByteEnumField('interface_numbering_subtype',
    640                       SUBTYPE_INTERFACE_NUMBER_UNKNOWN,
    641                       INTERFACE_NUMBERING_SUBTYPES),
    642         BitField('interface_number', 0, 32),
    643         BitFieldLenField('_oid_string_length', None, 8, length_of='object_id'),
    644         XStrLenField('object_id', '',
    645                      length_from=lambda pkt: pkt._oid_string_length),
    646     ]
    647 
    648     def _check(self):
    649         """
    650         run layer specific checks
    651         """
    652         if conf.contribs['LLDP'].strict_mode():
    653             management_address_len = len(self.management_address)
    654             if management_address_len == 0 or management_address_len > 31:
    655                 raise LLDPInvalidLengthField(
    656                     'management address must be  1..31 characters long - '
    657                     'got string of size {}'.format(management_address_len))
    658 
    659     def post_dissect(self, s):
    660         self._check()
    661         return super(LLDPDUManagementAddress, self).post_dissect(s)
    662 
    663     def do_build(self):
    664         self._check()
    665         return super(LLDPDUManagementAddress, self).do_build()
    666 
    667 LLDPDU_CLASS_TYPES = {
    668     0x00: LLDPDUEndOfLLDPDU,
    669     0x01: LLDPDUChassisID,
    670     0x02: LLDPDUPortID,
    671     0x03: LLDPDUTimeToLive,
    672     0x04: LLDPDUPortDescription,
    673     0x05: LLDPDUSystemName,
    674     0x06: LLDPDUSystemDescription,
    675     0x07: LLDPDUSystemCapabilities,
    676     0x08: LLDPDUManagementAddress,
    677     range(0x09, 0x7e): None,  # reserved - future standardization
    678     127: None  # organisation specific TLV
    679 }
    680 
    681 class LLDPConfiguration(object):
    682     """
    683     basic configuration for LLDP layer
    684     """
    685     def __init__(self):
    686         self._strict_mode = True
    687         self.strict_mode_enable()
    688 
    689     def strict_mode_enable(self):
    690         """
    691         enable strict mode and dissector debugging
    692         """
    693         self._strict_mode = True
    694         conf.debug_dissector = True
    695 
    696     def strict_mode_disable(self):
    697         """
    698         disable strict mode and dissector debugging
    699         """
    700         self._strict_mode = False
    701         conf.debug_dissector = False
    702 
    703     def strict_mode(self):
    704         """
    705         get current strict mode state
    706         """
    707         return self._strict_mode
    708 
    709 
    710 conf.contribs['LLDP'] = LLDPConfiguration()
    711 
    712 bind_layers(Ether, LLDPDU, type=LLDP_ETHER_TYPE)
    713 bind_layers(Dot1Q, LLDPDU, type=LLDP_ETHER_TYPE)
    714