/scapy/layers/lldp.py

https://bitbucket.org/secdev/scapy-com · Python · 242 lines · 163 code · 59 blank · 20 comment · 7 complexity · 6ecca5bc10d467fea5a6d441ff3b1a68 MD5 · raw file

  1. #!/usr/bin/env python
  2. ## This file is part of Scapy
  3. ## See http://www.secdev.org/projects/scapy for more informations
  4. ## Copyright (C) Philippe Biondi <phil@secdev.org>
  5. ## This program is published under a GPLv2 license
  6. ## Copyright (c) 2011 Jochen Bartl <jochen.bartl gmail com>
  7. """
  8. LLDP (Link Layer Discovery Protocol)
  9. """
  10. from scapy.packet import *
  11. from scapy.fields import *
  12. from scapy.layers.l2 import Ether
  13. from scapy.layers.inet6 import IP6Field
  14. _LLDP_tlv_cls = {0: "LLDPDUEnd",
  15. 1: "LLDPChassisId",
  16. 2: "LLDPPortId",
  17. 3: "LLDPTTL",
  18. 4: "LLDPPortDescription",
  19. 5: "LLDPSystemName",
  20. 6: "LLDPSystemDescription",
  21. 7: "LLDPSystemCapabilities",
  22. 8: "LLDPManagementAddress",
  23. 127: "LLDPOrganizationalSpecific"}
  24. _LLDP_tlv_types = {0: "End of LLDPDU",
  25. 1: "Chassis Id",
  26. 2: "Port Id",
  27. 3: "Time to Live",
  28. 4: "Port Description",
  29. 5: "System Name",
  30. 6: "System Description",
  31. 7: "System Capabilities",
  32. 8: "Management Address",
  33. 127: "Organization Specific"}
  34. # (oui, subtype)
  35. # 0x0080c2 - IEEE 802.1
  36. # 0x00120f - IEEE 802.3
  37. _LLDPOrgSpec_tlv_cls = {(0x0080c2, 0x01): "LLDPDot1PortVlanId",
  38. }
  39. def _LLDPGuessPacketClass(p=None, **kargs):
  40. if p is None:
  41. return LLDPGeneric(**kargs)
  42. cls = Raw
  43. if len(p) >= 2:
  44. t = struct.unpack("!B", p[0])[0]
  45. t = (0xfe & t) >> 1
  46. if t != 127:
  47. clsname = _LLDP_tlv_cls.get(t, "LLDPGeneric")
  48. else:
  49. oui = struct.unpack("!I", "\x00" + p[2:5])[0]
  50. subtype = struct.unpack("!B", p[5])[0]
  51. clsname = _LLDPOrgSpec_tlv_cls.get((oui, subtype), "LLDPOrgSpecGeneric")
  52. cls = globals()[clsname]
  53. return cls(p, **kargs)
  54. class LLDPGeneric(Packet):
  55. name = "LLDP Generic TLV"
  56. fields_desc = [BitField("type", 1, 7),
  57. BitFieldLenField("length", None, 9, length_of="value"),
  58. StrLenField("value", "", length_from=lambda x: x.length)]
  59. def guess_payload_class(self, p):
  60. return Padding
  61. def post_build(self, p, pay):
  62. if self.length is None:
  63. l = len(p) - 2
  64. p = chr((self.type << 1) ^ (l >> 8)) + chr(l & 0xff) + p[2:]
  65. return p+pay
  66. class LLDPOrgSpecGeneric(LLDPGeneric):
  67. name = "LLDP Org Spec Generic TLV"
  68. fields_desc = [BitField("type", 127, 7),
  69. BitFieldLenField("length", None, 9, length_of="value"),
  70. XThreeBytesField("oui", 0),
  71. ByteField("subtype", 0),
  72. StrLenField("value", "", length_from=lambda x: x.length - 4)]
  73. class LLDPDUEnd(LLDPGeneric):
  74. name = "End of LLDPDU"
  75. fields_desc = [BitField("type", 0, 7),
  76. BitField("length", 0, 9)]
  77. _LLDPChassisId_Subtypes = {0: "Reserved",
  78. 1: "Chassis component",
  79. 2: "Interface alias",
  80. 3: "Port component",
  81. 4: "MAC address",
  82. 5: "Network address",
  83. 6: "Interface name",
  84. 7: "Locally assigned"}
  85. class LLDPChassisId(LLDPGeneric):
  86. name = "LLDP Chassis"
  87. fields_desc = [BitField("type", 1, 7),
  88. BitField("length", None, 9),
  89. ByteEnumField("subtype", 4, _LLDPChassisId_Subtypes),
  90. ConditionalField(MACField("macaddr", "00:11:22:33:44:55"), lambda pkt: pkt.subtype == 4),
  91. # TODO Subtype 5, IPv4 / IPv6
  92. # Catch-all field for undefined subtypes
  93. ConditionalField(StrLenField("value", "", length_from=lambda x: x.length - 1),
  94. lambda pkt: pkt.subtype not in [4])]
  95. _LLDPPortId_Subtypes = {0: "Reserved",
  96. 1: "Interface alias",
  97. 2: "Port component",
  98. 3: "MAC address",
  99. 4: "Network address",
  100. 5: "Interface name",
  101. 6: "Agent circuit ID",
  102. 7: "Locally assigned"}
  103. class LLDPPortId(LLDPGeneric):
  104. name = "LLDP PortId"
  105. fields_desc = [BitField("type", 2, 7),
  106. BitField("length", None, 9),
  107. ByteEnumField("subtype", 3, _LLDPPortId_Subtypes),
  108. ConditionalField(MACField("macaddr", "00:11:22:33:44:55"), lambda pkt: pkt.subtype == 3),
  109. # TODO Subtype 4, IPv4 / IPv6
  110. # Catch-all field for undefined subtypes
  111. ConditionalField(StrLenField("value", "", length_from=lambda x: x.length - 1),
  112. lambda pkt: pkt.subtype not in [3])]
  113. class LLDPTTL(LLDPGeneric):
  114. name = "LLDP TTL"
  115. fields_desc = [BitField("type", 3, 7),
  116. BitField("length", None, 9),
  117. ShortField("seconds", 120)]
  118. class LLDPPortDescription(LLDPGeneric):
  119. name = "LLDP Port Description"
  120. type = 4
  121. value = "FastEthernet0/1"
  122. class LLDPSystemName(LLDPGeneric):
  123. name = "LLDP System Name"
  124. type = 5
  125. value = "Scapy"
  126. class LLDPSystemDescription(LLDPGeneric):
  127. name = "LLDP System Description"
  128. type = 6
  129. value = "Scapy"
  130. _LLDPSystemCapabilities = ["other", "repeater", "bridge", "wlanap", "router", "telephone", "docsiscable", "stationonly"]
  131. class LLDPSystemCapabilities(LLDPGeneric):
  132. name = "LLDP System Capabilities"
  133. fields_desc = [BitField("type", 7, 7),
  134. BitField("length", None, 9),
  135. # Available capabilities
  136. FlagsField("capabilities", 0, 16, _LLDPSystemCapabilities),
  137. # Enabled capabilities
  138. FlagsField("enabled", 0, 16, _LLDPSystemCapabilities)]
  139. _LLDPManagementAddress_Subtype = {1: "IPv4",
  140. 2: "IPv6",
  141. 6: "802"
  142. }
  143. _LLDPManagementAddress_IfSubtype = {1: "Unknown",
  144. 2: "ifIndex",
  145. 3: "System Port Number"
  146. }
  147. class LLDPManagementAddress(LLDPGeneric):
  148. name = "LLDP Management Address"
  149. fields_desc = [BitField("type", 8, 7),
  150. BitField("length", None, 9),
  151. ByteField("addrlen", None),
  152. ByteEnumField("addrsubtype", 1, _LLDPManagementAddress_Subtype),
  153. ConditionalField(IPField("ipaddr", "192.168.0.1"), lambda pkt: pkt.addrsubtype == 1),
  154. ConditionalField(IP6Field("ip6addr", "2001:db8::1"), lambda pkt: pkt.addrsubtype == 2),
  155. ConditionalField(MACField("macaddr", "00:11:22:33:44:55"), lambda pkt: pkt.addrsubtype == 6),
  156. ConditionalField(StrLenField("addrval", "", length_from=lambda x: x.addrlen - 1),
  157. lambda pkt: pkt.addrsubtype not in [1, 2, 6]),
  158. ByteEnumField("ifsubtype", 2, _LLDPManagementAddress_IfSubtype),
  159. IntField("ifnumber", 0),
  160. FieldLenField("oidlen", None, length_of="oid", fmt="B"),
  161. StrLenField("oid", "", length_from=lambda x: x.oidlen)]
  162. def post_build(self, p, pay):
  163. # TODO Remove redundant code. LLDPGeneric.post_build()
  164. if self.length is None:
  165. l = len(p) - 2
  166. p = chr((self.type << 1) ^ (l >> 8)) + chr(l & 0xff) + p[2:]
  167. if self.addrlen is None:
  168. addrlen = len(p) - 2 - 8 - len(self.oid) + 1
  169. p = p[:2] + struct.pack("B", addrlen) + p[3:]
  170. return p+pay
  171. _LLDPDot1Subtype = {1: "Port VLAN Id"}
  172. class LLDPDot1PortVlanId(LLDPOrgSpecGeneric):
  173. name = "LLDP IEEE 802.1 Port VLAN Id"
  174. fields_desc = [BitField("type", 127, 7),
  175. BitField("length", None, 9),
  176. # TODO: XThreeBytesEnumField
  177. XThreeBytesField("oui", 0x0080c2),
  178. ByteEnumField("subtype", 0x01, _LLDPDot1Subtype),
  179. ShortField("vlan", 1)]
  180. class LLDP(Packet):
  181. name ="LLDP"
  182. fields_desc = [PacketListField("tlvlist", [], _LLDPGuessPacketClass)]
  183. bind_layers(Ether, LLDP, type=0x88cc)