sctp.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. """
  2. Stream Control Transmission Protocol.
  3. http://tools.ietf.org/html/rfc3286
  4. http://tools.ietf.org/html/rfc2960
  5. """
  6. from pypacker import pypacker, triggerlist
  7. from pypacker import checksum
  8. import struct
  9. import logging
  10. logger = logging.getLogger("pypacker")
  11. # Chunk Types
  12. DATA = 0
  13. INIT = 1
  14. INIT_ACK = 2
  15. SACK = 3
  16. HEARTBEAT = 4
  17. HEARTBEAT_ACK = 5
  18. ABORT = 6
  19. SHUTDOWN = 7
  20. SHUTDOWN_ACK = 8
  21. ERROR = 9
  22. COOKIE_ECHO = 10
  23. COOKIE_ACK = 11
  24. ECNE = 12
  25. CWR = 13
  26. SHUTDOWN_COMPLETE = 14
  27. class Chunk(pypacker.Packet):
  28. __hdr__ = (
  29. ("type", "B", INIT),
  30. ("flags", "B", 0),
  31. ("len", "H", 0) # length of header + data = 4 + x Bytes
  32. )
  33. class SCTP(pypacker.Packet):
  34. __hdr__ = (
  35. ("sport", "H", 0),
  36. ("dport", "H", 0),
  37. ("vtag", "I", 0),
  38. ("sum", "I", 0),
  39. ("chunks", None, triggerlist.TriggerList)
  40. )
  41. # handle padding attribute
  42. def __get_padding(self):
  43. try:
  44. return self._padding
  45. except:
  46. return b""
  47. def __set_padding(self, padding):
  48. self._padding = padding
  49. padding = property(__get_padding, __set_padding)
  50. def _dissect(self, buf):
  51. # parse chunks
  52. chunks = []
  53. off = 12
  54. blen = len(buf)
  55. # logger.debug("SCTP: parsing chunks")
  56. chunktype = -1
  57. # TODO: use lazy dissect
  58. while off + 4 < blen:
  59. dlen = struct.unpack(">H", buf[off + 2: off + 4])[0]
  60. # check for padding (this should be a data chunk)
  61. if off + dlen < blen:
  62. self.padding = buf[off + dlen:]
  63. # logger.debug("found padding: %s" % self.padding)
  64. chunk = Chunk(buf[off: off + dlen])
  65. # logger.debug("SCTP: Chunk; %s " % chunk)
  66. chunks.append(chunk)
  67. # get payload chunktype from DATA chunks
  68. if chunk.type == 0:
  69. chunktype = struct.unpack(">I",
  70. buf[off + chunk.header_len + 8: off + chunk.header_len + 8 + 4]
  71. )[0]
  72. # logger.debug("got DATA chunk, chunktype: %d" % chunktype)
  73. # remove data from chunk: use bytes for handler
  74. chunk.body_bytes = b""
  75. off += len(chunk)
  76. # assume DATA is the last chunk
  77. break
  78. off += dlen
  79. # TODO: use lazy dissect, possible?
  80. self.chunks.extend(chunks)
  81. chunktype = struct.unpack(">H", buf[2: 4])[0]
  82. self._init_handler(chunktype, buf[off:-len(self.padding)])
  83. # TODO: return length wothout dissecting
  84. return off
  85. def bin(self, update_auto_fields=True):
  86. if update_auto_fields and self._changed():
  87. # logger.debug("updating checksum")
  88. self._calc_sum()
  89. return pypacker.Packet.bin(self, update_auto_fields=update_auto_fields) + self.padding
  90. def _calc_sum(self):
  91. # mark as changed
  92. self.sum = 0
  93. s = checksum.crc32_add(0xffffffff, self._pack_header())
  94. padlen = len(self.padding)
  95. if padlen == 0:
  96. s = checksum.crc32_add(s, self.body_bytes)
  97. else:
  98. # logger.debug("checksum with padding")
  99. s = checksum.crc32_add(s, self.body_bytes[:-padlen])
  100. sum = checksum.crc32_done(s)
  101. # logger.debug("sum is: %d" % sum)
  102. self.sum = sum
  103. def direction(self, other):
  104. # logger.debug("checking direction: %s<->%s" % (self, other))
  105. if self.sport == other.sport and self.dport == other.dport:
  106. # consider packet to itself: can be DIR_REV
  107. return pypacker.Packet.DIR_SAME | pypacker.Packet.DIR_REV
  108. elif self.sport == other.dport and self.dport == other.sport:
  109. return pypacker.Packet.DIR_REV
  110. else:
  111. return pypacker.Packet.DIR_UNKNOWN
  112. def reverse_address(self):
  113. self.sport, self.dport = self.dport, self.sport
  114. # load handler
  115. from pypacker.layer567 import diameter
  116. pypacker.Packet.load_handler(SCTP,
  117. {
  118. 123: diameter.Diameter,
  119. }
  120. )