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