ethernet.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. """
  2. Ethernet II, IEEE 802.3
  3. RFC 1042
  4. """
  5. from pypacker import pypacker
  6. import logging
  7. import struct
  8. # avoid unneeded references for performance reasons
  9. pack = struct.pack
  10. unpack = struct.unpack
  11. logger = logging.getLogger("pypacker")
  12. ETH_CRC_LEN = 4
  13. ETH_HDR_LEN = 14
  14. ETH_LEN_MIN = 64 # minimum frame length with CRC
  15. ETH_LEN_MAX = 1518 # maximum frame length with CRC
  16. ETH_MTU = (ETH_LEN_MAX - ETH_HDR_LEN - ETH_CRC_LEN)
  17. ETH_MIN = (ETH_LEN_MIN - ETH_HDR_LEN - ETH_CRC_LEN)
  18. # Ethernet payload types - http://standards.ieee.org/regauth/ethertype
  19. ETH_TYPE_PUP = 0x0200 # PUP protocol
  20. ETH_TYPE_IP = 0x0800 # IPv4 protocol
  21. ETH_TYPE_ARP = 0x0806 # address resolution protocol
  22. ETH_TYPE_WOL = 0x0842 # Wake on LAN
  23. ETH_TYPE_CDP = 0x2000 # Cisco Discovery Protocol
  24. ETH_TYPE_DTP = 0x2004 # Cisco Dynamic Trunking Protocol
  25. ETH_TYPE_REVARP = 0x8035 # reverse addr resolution protocol
  26. ETH_TYPE_ETHTALK = 0x809B # Apple Talk
  27. ETH_TYPE_AARP = 0x80F3 # Appletalk Address Resolution Protocol
  28. ETH_TYPE_8021Q = 0x8100 # IEEE 802.1Q VLAN tagging
  29. ETH_TYPE_IPX = 0x8137 # Internetwork Packet Exchange
  30. ETH_TYPE_NOV = 0x8138 # Novell
  31. ETH_TYPE_IP6 = 0x86DD # IPv6 protocol
  32. ETH_TYPE_MPLS_UCAST = 0x8847 # MPLS unicast
  33. ETH_TYPE_MPLS_MCAST = 0x8848 # MPLS multicast
  34. ETH_TYPE_PPOE_DISC = 0x8863 # PPPoE Discovery
  35. ETH_TYPE_PPOE_SESS = 0x8864 # PPPoE Session
  36. ETH_TYPE_JUMBOF = 0x8870 # Jumbo Frames
  37. ETH_TYPE_PROFINET = 0x8892 # Realtime-Ethernet PROFINET
  38. ETH_TYPE_ATAOE = 0x88A2 # ATA other Ethernet
  39. ETH_TYPE_ETHERCAT = 0x88A4 # Realtime-Ethernet Ethercat
  40. ETH_TYPE_PBRIDGE = 0x88A8 # Provider Briding
  41. ETH_TYPE_POWERLINK = 0x88AB # Realtime Ethernet POWERLINK
  42. ETH_TYPE_LLDP = 0x88CC # Link Layer Discovery Protocol
  43. ETH_TYPE_SERCOS = 0x88CD # Realtime Ethernet SERCOS III
  44. ETH_TYPE_FIBRE_ETH = 0x8906 # Fibre Channel over Ethernet
  45. ETH_TYPE_FCOE = 0x8914 # FCoE Initialization Protocol (FIP)
  46. ETH_TYPE_LLC = 0xFFFFF
  47. # MPLS label stack fields
  48. MPLS_LABEL_MASK = 0xfffff000
  49. MPLS_QOS_MASK = 0x00000e00
  50. MPLS_TTL_MASK = 0x000000ff
  51. MPLS_LABEL_SHIFT = 12
  52. MPLS_QOS_SHIFT = 9
  53. MPLS_TTL_SHIFT = 0
  54. MPLS_STACK_BOTTOM = 0x0100
  55. class Ethernet(pypacker.Packet):
  56. __hdr__ = (
  57. ("dst", "6s", b"\xff" * 6),
  58. ("src", "6s", b"\xff" * 6),
  59. ("vlan", "4s", None),
  60. # ("len", "H", None),
  61. ("type", "H", ETH_TYPE_IP) # type = Ethernet II, len = 802.3
  62. )
  63. dst_s = pypacker.get_property_mac("dst")
  64. src_s = pypacker.get_property_mac("src")
  65. def _dissect(self, buf):
  66. hlen = 14
  67. # we need to check for VLAN TPID here (0x8100) to get correct header-length
  68. if buf[12:14] == b"\x81\x00":
  69. # logger.debug(">>> got vlan tag")
  70. self.vlan = buf[12:16]
  71. # logger.debug("re-extracting field: %s" % self.vlan)
  72. hlen = 18
  73. # check for DSAP via length
  74. type_len = unpack(">H", buf[12: 14])[0]
  75. if type_len < 1536:
  76. # assume DSAP is following (802.2 DSAP)
  77. # self.len = type_len
  78. # deactivate eth_type field
  79. # logger.debug(">>> deactivating type")
  80. self.type = None
  81. self._init_handler(ETH_TYPE_LLC, buf[12: 14])
  82. return
  83. # avoid calling unpack more than once
  84. eth_type = unpack(">H", buf[hlen - 2: hlen])[0]
  85. # logger.debug("hlen is: %d" % eth_type)
  86. # handle ethernet-padding: remove it but save for later use
  87. # don't use headers for this because this is a rare situation
  88. dlen = len(buf) - hlen # data length [+ padding?]
  89. try:
  90. # this will only work on complete headers: Ethernet + IP + ...
  91. # handle padding using IPv4, IPv6
  92. # TODO: check for other protocols
  93. # logger.debug(">>> checking for padding")
  94. if eth_type == ETH_TYPE_IP:
  95. dlen_ip = unpack(">H", buf[hlen + 2: hlen + 4])[0] # real data length
  96. if dlen_ip < dlen:
  97. # padding found
  98. # logger.debug("got padding for IPv4")
  99. self._padding = buf[hlen + dlen_ip:]
  100. dlen = dlen_ip
  101. # handle padding using IPv6
  102. # IPv6 is a piece of sh$§! payloadlength = exclusive standard header, INCLUSIVE options!
  103. elif eth_type == ETH_TYPE_IP6:
  104. dlen_ip = unpack(">H", buf[hlen + 4: hlen + 6])[0] # real data length
  105. if 40 + dlen_ip < dlen:
  106. # padding found
  107. # logger.debug("got padding for IPv6")
  108. self._padding = buf[hlen + dlen_ip:]
  109. dlen = dlen_ip
  110. except struct.error:
  111. # logger.debug("could not extract padding info, assuming incomplete ethernet frame")
  112. pass
  113. except:
  114. logger.exception("could not extract padding info")
  115. self._init_handler(eth_type, buf[hlen: hlen + dlen])
  116. return hlen
  117. def bin(self, update_auto_fields=True):
  118. """Custom bin(): handle padding for Ethernet."""
  119. return pypacker.Packet.bin(self, update_auto_fields=update_auto_fields) + self.padding
  120. def __len__(self):
  121. return super().__len__() + len(self.padding)
  122. def direction(self, other):
  123. # logger.debug("checking direction: %s<->%s" % (self, other))
  124. if self.dst == other.dst and self.src == other.src:
  125. # consider packet to itself: can be DIR_REV
  126. return pypacker.Packet.DIR_SAME | pypacker.Packet.DIR_REV
  127. elif (self.dst == other.src and self.src == other.dst) or\
  128. (self.dst == b"\xff\xff\xff\xff\xff\xff" and other.dst == self.src): # broadcast
  129. return pypacker.Packet.DIR_REV
  130. else:
  131. return pypacker.Packet.DIR_UNKNOWN
  132. # handle padding attribute
  133. def __get_padding(self):
  134. try:
  135. return self._padding
  136. except AttributeError:
  137. return b""
  138. def __set_padding(self, padding):
  139. self._padding = padding
  140. padding = property(__get_padding, __set_padding)
  141. def reverse_address(self):
  142. self.dst, self.src = self.src, self.dst
  143. # load handler
  144. from pypacker.layer12 import arp, dtp, pppoe, llc
  145. from pypacker.layer3 import ip, ip6, ipx
  146. pypacker.Packet.load_handler(Ethernet,
  147. {
  148. ETH_TYPE_IP: ip.IP,
  149. ETH_TYPE_ARP: arp.ARP,
  150. ETH_TYPE_DTP: dtp.DTP,
  151. ETH_TYPE_IPX: ipx.IPX,
  152. ETH_TYPE_IP6: ip6.IP6,
  153. ETH_TYPE_PPOE_DISC: pppoe.PPPoE,
  154. ETH_TYPE_PPOE_SESS: pppoe.PPPoE,
  155. ETH_TYPE_LLC: llc.LLC,
  156. }
  157. )