stackforge 0.4.0__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. {stackforge-0.4.0 → stackforge-0.6.0}/Cargo.lock +3 -3
  2. {stackforge-0.4.0 → stackforge-0.6.0}/Cargo.toml +1 -1
  3. {stackforge-0.4.0 → stackforge-0.6.0}/PKG-INFO +42 -3
  4. {stackforge-0.4.0 → stackforge-0.6.0}/README.md +41 -2
  5. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/flow/config.rs +8 -0
  6. stackforge-0.6.0/crates/stackforge-core/src/flow/icmp_state.rs +144 -0
  7. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/flow/key.rs +53 -1
  8. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/flow/mod.rs +3 -1
  9. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/flow/state.rs +41 -4
  10. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/flow/table.rs +28 -1
  11. {stackforge-0.4.0 → stackforge-0.6.0}/src/lib.rs +88 -0
  12. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_flow.py +135 -0
  13. stackforge-0.6.0/tests/python/test_max_packet_flow_len.py +252 -0
  14. {stackforge-0.4.0 → stackforge-0.6.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  15. {stackforge-0.4.0 → stackforge-0.6.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  16. {stackforge-0.4.0 → stackforge-0.6.0}/.github/workflows/release.yml +0 -0
  17. {stackforge-0.4.0 → stackforge-0.6.0}/.github/workflows/test.yml +0 -0
  18. {stackforge-0.4.0 → stackforge-0.6.0}/.gitignore +0 -0
  19. {stackforge-0.4.0 → stackforge-0.6.0}/.pre-commit-config.yaml +0 -0
  20. {stackforge-0.4.0 → stackforge-0.6.0}/.python-version +0 -0
  21. {stackforge-0.4.0 → stackforge-0.6.0}/CLAUDE.md +0 -0
  22. {stackforge-0.4.0 → stackforge-0.6.0}/CODE_OF_CONDUCT.md +0 -0
  23. {stackforge-0.4.0 → stackforge-0.6.0}/CONTRIBUTING.md +0 -0
  24. {stackforge-0.4.0 → stackforge-0.6.0}/LICENSE +0 -0
  25. {stackforge-0.4.0 → stackforge-0.6.0}/cliff.toml +0 -0
  26. {stackforge-0.4.0 → stackforge-0.6.0}/cog.toml +0 -0
  27. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-automata/Cargo.toml +0 -0
  28. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-automata/src/lib.rs +0 -0
  29. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/Cargo.toml +0 -0
  30. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/error.rs +0 -0
  31. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/flow/error.rs +0 -0
  32. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/flow/tcp_reassembly.rs +0 -0
  33. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/flow/tcp_state.rs +0 -0
  34. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/flow/udp_state.rs +0 -0
  35. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/arp.rs +0 -0
  36. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/bindings.rs +0 -0
  37. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dns/bitmap.rs +0 -0
  38. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dns/builder.rs +0 -0
  39. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dns/edns.rs +0 -0
  40. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dns/header.rs +0 -0
  41. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dns/mod.rs +0 -0
  42. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dns/query.rs +0 -0
  43. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dns/rdata.rs +0 -0
  44. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dns/rr.rs +0 -0
  45. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dns/svcb.rs +0 -0
  46. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dns/types.rs +0 -0
  47. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot11/builder.rs +0 -0
  48. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot11/control.rs +0 -0
  49. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot11/data.rs +0 -0
  50. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot11/ie.rs +0 -0
  51. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot11/management.rs +0 -0
  52. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot11/mod.rs +0 -0
  53. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot11/radiotap.rs +0 -0
  54. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot11/security.rs +0 -0
  55. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot11/types.rs +0 -0
  56. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot15d4/beacon.rs +0 -0
  57. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot15d4/builder.rs +0 -0
  58. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot15d4/command.rs +0 -0
  59. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot15d4/crc.rs +0 -0
  60. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot15d4/mod.rs +0 -0
  61. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot15d4/security.rs +0 -0
  62. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/dot15d4/types.rs +0 -0
  63. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ethernet.rs +0 -0
  64. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/field.rs +0 -0
  65. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/field_ext.rs +0 -0
  66. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ftp/builder.rs +0 -0
  67. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ftp/mod.rs +0 -0
  68. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/generic/builder.rs +0 -0
  69. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/generic/mod.rs +0 -0
  70. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/http/builder.rs +0 -0
  71. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/http/detection.rs +0 -0
  72. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/http/mod.rs +0 -0
  73. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/http/request.rs +0 -0
  74. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/http/response.rs +0 -0
  75. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/http2/builder.rs +0 -0
  76. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/http2/frames.rs +0 -0
  77. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/http2/hpack.rs +0 -0
  78. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/http2/mod.rs +0 -0
  79. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/icmp/builder.rs +0 -0
  80. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/icmp/checksum.rs +0 -0
  81. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/icmp/error.rs +0 -0
  82. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/icmp/extensions.rs +0 -0
  83. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/icmp/mod.rs +0 -0
  84. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/icmp/types.rs +0 -0
  85. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/icmpv6/builder.rs +0 -0
  86. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/icmpv6/mod.rs +0 -0
  87. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/imap/builder.rs +0 -0
  88. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/imap/mod.rs +0 -0
  89. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ipv4/builder.rs +0 -0
  90. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ipv4/checksum.rs +0 -0
  91. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ipv4/fragmentation.rs +0 -0
  92. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ipv4/header.rs +0 -0
  93. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ipv4/mod.rs +0 -0
  94. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ipv4/options.rs +0 -0
  95. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ipv4/protocol.rs +0 -0
  96. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ipv4/routing.rs +0 -0
  97. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ipv4/ttl.rs +0 -0
  98. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ipv6/builder.rs +0 -0
  99. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ipv6/ext_headers.rs +0 -0
  100. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ipv6/mod.rs +0 -0
  101. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/l2tp/builder.rs +0 -0
  102. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/l2tp/mod.rs +0 -0
  103. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/mod.rs +0 -0
  104. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/modbus/builder.rs +0 -0
  105. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/modbus/crc.rs +0 -0
  106. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/modbus/mod.rs +0 -0
  107. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/mqtt/builder.rs +0 -0
  108. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/mqtt/mod.rs +0 -0
  109. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/mqttsn/builder.rs +0 -0
  110. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/mqttsn/mod.rs +0 -0
  111. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/neighbor.rs +0 -0
  112. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/pop3/builder.rs +0 -0
  113. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/pop3/mod.rs +0 -0
  114. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/quic/builder.rs +0 -0
  115. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/quic/frames.rs +0 -0
  116. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/quic/headers.rs +0 -0
  117. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/quic/mod.rs +0 -0
  118. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/quic/varint.rs +0 -0
  119. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/raw.rs +0 -0
  120. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/smtp/builder.rs +0 -0
  121. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/smtp/mod.rs +0 -0
  122. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ssh/builder.rs +0 -0
  123. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/ssh/mod.rs +0 -0
  124. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/stack.rs +0 -0
  125. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tcp/builder.rs +0 -0
  126. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tcp/checksum.rs +0 -0
  127. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tcp/flags.rs +0 -0
  128. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tcp/header.rs +0 -0
  129. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tcp/mod.rs +0 -0
  130. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tcp/options.rs +0 -0
  131. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tcp/services.rs +0 -0
  132. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tftp/builder.rs +0 -0
  133. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tftp/mod.rs +0 -0
  134. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/builder.rs +0 -0
  135. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/cert.rs +0 -0
  136. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/crypto/cipher_aead.rs +0 -0
  137. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/crypto/cipher_block.rs +0 -0
  138. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/crypto/cipher_stream.rs +0 -0
  139. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/crypto/compression.rs +0 -0
  140. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/crypto/groups.rs +0 -0
  141. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/crypto/hash.rs +0 -0
  142. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/crypto/hkdf_tls13.rs +0 -0
  143. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/crypto/hmac_tls.rs +0 -0
  144. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/crypto/mod.rs +0 -0
  145. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/crypto/prf.rs +0 -0
  146. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/crypto/suites.rs +0 -0
  147. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/extensions/common.rs +0 -0
  148. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/extensions/key_share.rs +0 -0
  149. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/extensions/mod.rs +0 -0
  150. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/extensions/signature_algorithms.rs +0 -0
  151. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/extensions/sni.rs +0 -0
  152. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/extensions/supported_versions.rs +0 -0
  153. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/handshake/certificate.rs +0 -0
  154. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/handshake/client_hello.rs +0 -0
  155. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/handshake/finished.rs +0 -0
  156. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/handshake/key_exchange.rs +0 -0
  157. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/handshake/mod.rs +0 -0
  158. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/handshake/server_hello.rs +0 -0
  159. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/keyexchange/ecdhe.rs +0 -0
  160. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/keyexchange/mod.rs +0 -0
  161. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/mod.rs +0 -0
  162. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/record.rs +0 -0
  163. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/session.rs +0 -0
  164. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/sslv2/handshake.rs +0 -0
  165. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/sslv2/mod.rs +0 -0
  166. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/sslv2/record.rs +0 -0
  167. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/tls/types.rs +0 -0
  168. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/udp/builder.rs +0 -0
  169. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/udp/checksum.rs +0 -0
  170. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/udp/mod.rs +0 -0
  171. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/zwave/builder.rs +0 -0
  172. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/layer/zwave/mod.rs +0 -0
  173. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/lib.rs +0 -0
  174. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/packet.rs +0 -0
  175. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/pcap/mod.rs +0 -0
  176. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/pcap/reader.rs +0 -0
  177. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/pcap/writer.rs +0 -0
  178. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/utils/bits.rs +0 -0
  179. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/utils/checksum.rs +0 -0
  180. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/utils/compare.rs +0 -0
  181. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/utils/hex.rs +0 -0
  182. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/utils/mod.rs +0 -0
  183. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/utils/padding.rs +0 -0
  184. {stackforge-0.4.0 → stackforge-0.6.0}/crates/stackforge-core/src/utils/random.rs +0 -0
  185. {stackforge-0.4.0 → stackforge-0.6.0}/pyproject.toml +0 -0
  186. {stackforge-0.4.0 → stackforge-0.6.0}/python/stackforge/__init__.py +0 -0
  187. {stackforge-0.4.0 → stackforge-0.6.0}/python/stackforge/custom.py +0 -0
  188. {stackforge-0.4.0 → stackforge-0.6.0}/rust-toolchain.toml +0 -0
  189. {stackforge-0.4.0 → stackforge-0.6.0}/rustfmt.toml +0 -0
  190. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/arp.rs +0 -0
  191. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/compat_tests/arp_compat.rs +0 -0
  192. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/compat_tests/ethernet_compat.rs +0 -0
  193. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/compat_tests/icmp_compat.rs +0 -0
  194. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/compat_tests/ipv4_compat.rs +0 -0
  195. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/compat_tests/mod.rs +0 -0
  196. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/compat_tests/ssh_compat.rs +0 -0
  197. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/compat_tests/tcp_compat.rs +0 -0
  198. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/compat_tests/tls_compat.rs +0 -0
  199. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/compat_tests/udp_compat.rs +0 -0
  200. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/dns.rs +0 -0
  201. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/dot11.rs +0 -0
  202. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/dot15d4.rs +0 -0
  203. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/ethernet.rs +0 -0
  204. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/field.rs +0 -0
  205. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/ftp.rs +0 -0
  206. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/http.rs +0 -0
  207. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/http2.rs +0 -0
  208. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/icmpv6.rs +0 -0
  209. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/imap.rs +0 -0
  210. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/ipv6.rs +0 -0
  211. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/layer.rs +0 -0
  212. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/main.rs +0 -0
  213. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/modbus.rs +0 -0
  214. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/mqtt.rs +0 -0
  215. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/packet.rs +0 -0
  216. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/pop3.rs +0 -0
  217. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/quic.rs +0 -0
  218. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/smtp.rs +0 -0
  219. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/ssh.rs +0 -0
  220. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/tftp.rs +0 -0
  221. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/tls.rs +0 -0
  222. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/util.rs +0 -0
  223. {stackforge-0.4.0 → stackforge-0.6.0}/tests/integration/zwave.rs +0 -0
  224. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/scapy_compat/__init__.py +0 -0
  225. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/scapy_compat/conftest.py +0 -0
  226. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/scapy_compat/test_arp_compat.py +0 -0
  227. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/scapy_compat/test_checksums_compat.py +0 -0
  228. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/scapy_compat/test_edge_cases_compat.py +0 -0
  229. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/scapy_compat/test_ethernet_compat.py +0 -0
  230. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/scapy_compat/test_icmp_compat.py +0 -0
  231. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/scapy_compat/test_ipv4_compat.py +0 -0
  232. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/scapy_compat/test_pcap_compat.py +0 -0
  233. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/scapy_compat/test_stacking_compat.py +0 -0
  234. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/scapy_compat/test_tcp_compat.py +0 -0
  235. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/scapy_compat/test_udp_compat.py +0 -0
  236. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/scapy_compat/test_uts_integration.py +0 -0
  237. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_core.py +0 -0
  238. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_custom.py +0 -0
  239. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_dns.py +0 -0
  240. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_dot11.py +0 -0
  241. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_dot15d4.py +0 -0
  242. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_ftp.py +0 -0
  243. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_http.py +0 -0
  244. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_icmpv6.py +0 -0
  245. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_imap.py +0 -0
  246. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_ipv6.py +0 -0
  247. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_l2tp.py +0 -0
  248. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_modbus.py +0 -0
  249. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_mqtt.py +0 -0
  250. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_mqttsn.py +0 -0
  251. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_pcap.py +0 -0
  252. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_pop3.py +0 -0
  253. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_quic.py +0 -0
  254. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_smtp.py +0 -0
  255. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_ssh.py +0 -0
  256. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_tftp.py +0 -0
  257. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_tls.py +0 -0
  258. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_udp_icmp.py +0 -0
  259. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_uts_ftp.py +0 -0
  260. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_uts_http.py +0 -0
  261. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_uts_http2.py +0 -0
  262. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_uts_imap.py +0 -0
  263. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_uts_ipv6.py +0 -0
  264. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_uts_l2tp.py +0 -0
  265. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_uts_modbus.py +0 -0
  266. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_uts_mqtt.py +0 -0
  267. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_uts_mqttsn.py +0 -0
  268. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_uts_pop3.py +0 -0
  269. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_uts_quic.py +0 -0
  270. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_uts_smtp.py +0 -0
  271. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_uts_tftp.py +0 -0
  272. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_uts_zwave.py +0 -0
  273. {stackforge-0.4.0 → stackforge-0.6.0}/tests/python/test_zwave.py +0 -0
  274. {stackforge-0.4.0 → stackforge-0.6.0}/tests/sample_pcap/http2_h2c.pcap +0 -0
  275. {stackforge-0.4.0 → stackforge-0.6.0}/tests/sample_pcap/http_chunk.pcap +0 -0
  276. {stackforge-0.4.0 → stackforge-0.6.0}/tests/sample_pcap/http_compressed-brotli.pcap +0 -0
  277. {stackforge-0.4.0 → stackforge-0.6.0}/tests/sample_pcap/http_compressed-zstd.pcap +0 -0
  278. {stackforge-0.4.0 → stackforge-0.6.0}/tests/sample_pcap/http_compressed.pcap +0 -0
  279. {stackforge-0.4.0 → stackforge-0.6.0}/tests/sample_pcap/http_content_length.pcap +0 -0
  280. {stackforge-0.4.0 → stackforge-0.6.0}/tests/sample_pcap/http_head.pcapng +0 -0
  281. {stackforge-0.4.0 → stackforge-0.6.0}/tests/sample_pcap/http_tcp_psh.pcap +0 -0
  282. {stackforge-0.4.0 → stackforge-0.6.0}/tests/sample_pcap/ssh_ed25519.pcap +0 -0
  283. {stackforge-0.4.0 → stackforge-0.6.0}/tests/sample_pcap/tcprelay/PCAP.md +0 -0
  284. {stackforge-0.4.0 → stackforge-0.6.0}/tests/sample_pcap/tcprelay/smallFlows.pcap +0 -0
  285. {stackforge-0.4.0 → stackforge-0.6.0}/tests/sample_pcap/tcprelay/test.pcap +0 -0
  286. {stackforge-0.4.0 → stackforge-0.6.0}/tests/scripts/compare_packets.py +0 -0
  287. {stackforge-0.4.0 → stackforge-0.6.0}/tests/scripts/generate_scapy_fixtures.py +0 -0
  288. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/dns.uts +0 -0
  289. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/dns_dnssec.uts +0 -0
  290. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/dns_edns0.uts +0 -0
  291. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/dot11.uts +0 -0
  292. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/dot15d4.uts +0 -0
  293. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/fields.uts +0 -0
  294. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/ftp.uts +0 -0
  295. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/http.uts +0 -0
  296. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/http2.uts +0 -0
  297. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/imap.uts +0 -0
  298. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/inet.uts +0 -0
  299. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/inet6 copy.uts +0 -0
  300. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/inet6.uts +0 -0
  301. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/l2.uts +0 -0
  302. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/l2tp.uts +0 -0
  303. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/modbus.uts +0 -0
  304. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/mqtt.uts +0 -0
  305. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/mqttsn.uts +0 -0
  306. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/pop3.uts +0 -0
  307. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/quic.uts +0 -0
  308. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/smtp.uts +0 -0
  309. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/ssh.uts +0 -0
  310. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tftp.uts +0 -0
  311. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/__init__.py +0 -0
  312. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/cert.uts +0 -0
  313. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/example_client.py +0 -0
  314. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/example_server.py +0 -0
  315. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/pki/README.md +0 -0
  316. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/pki/ca_cert.pem +0 -0
  317. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/pki/ca_key.pem +0 -0
  318. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/pki/cli_cert.pem +0 -0
  319. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/pki/cli_key.pem +0 -0
  320. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/pki/srv_cert.pem +0 -0
  321. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/pki/srv_cert_ed25519.pem +0 -0
  322. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/pki/srv_key.pem +0 -0
  323. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/pki/srv_key_ed25519.pem +0 -0
  324. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/sslv2.uts +0 -0
  325. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/tls.uts +0 -0
  326. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/tls13.uts +0 -0
  327. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/tls/tlsclientserver.uts +0 -0
  328. {stackforge-0.4.0 → stackforge-0.6.0}/tests/uts/zwave.uts +0 -0
  329. {stackforge-0.4.0 → stackforge-0.6.0}/uv.lock +0 -0
@@ -1030,7 +1030,7 @@ dependencies = [
1030
1030
 
1031
1031
  [[package]]
1032
1032
  name = "stackforge"
1033
- version = "0.4.0"
1033
+ version = "0.6.0"
1034
1034
  dependencies = [
1035
1035
  "pyo3",
1036
1036
  "stackforge-automata",
@@ -1039,7 +1039,7 @@ dependencies = [
1039
1039
 
1040
1040
  [[package]]
1041
1041
  name = "stackforge-automata"
1042
- version = "0.4.0"
1042
+ version = "0.6.0"
1043
1043
  dependencies = [
1044
1044
  "bytes",
1045
1045
  "stackforge-core",
@@ -1047,7 +1047,7 @@ dependencies = [
1047
1047
 
1048
1048
  [[package]]
1049
1049
  name = "stackforge-core"
1050
- version = "0.4.0"
1050
+ version = "0.6.0"
1051
1051
  dependencies = [
1052
1052
  "aes",
1053
1053
  "aes-gcm",
@@ -21,7 +21,7 @@ shared-version = true
21
21
  tag-name = "v{{version}}"
22
22
 
23
23
  [workspace.package]
24
- version = "0.4.0"
24
+ version = "0.6.0"
25
25
  edition = "2024"
26
26
  license = "GPL-3.0-only"
27
27
  authors = ["Stackforge Contributors"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stackforge
3
- Version: 0.4.0
3
+ Version: 0.6.0
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: Intended Audience :: System Administrators
@@ -35,8 +35,8 @@ Project-URL: Repository, https://github.com/LaBackDoor/stackforge
35
35
 
36
36
  - **Scapy-style API** — Stack layers with `Ether() / IP() / TCP()`, set fields with keyword arguments
37
37
  - **High Performance** — Core logic in Rust, zero-copy parsing, copy-on-write mutation
38
- - **Broad Protocol Support** — Ethernet, ARP, IPv4/IPv6, TCP, UDP, ICMP, ICMPv6, DNS, HTTP/1.x, HTTP/2, QUIC, L2TP, 802.11 (Wi-Fi), 802.15.4 (Zigbee), and custom protocols
39
- - **Stateful Flow Extraction** — Extract bidirectional conversations from PCAP files with TCP state tracking, stream reassembly, and UDP timeout handling
38
+ - **Broad Protocol Support** — Ethernet, ARP, IPv4/IPv6, TCP, UDP, ICMP/ICMPv6 (with echo correlation), DNS, HTTP/1.x, HTTP/2, QUIC, L2TP, 802.11 (Wi-Fi), 802.15.4 (Zigbee), and custom protocols
39
+ - **Stateful Flow Extraction** — Extract bidirectional conversations from PCAP files with TCP state tracking, stream reassembly, UDP timeout handling, and optional max packet/flow length tracking
40
40
  - **PCAP I/O** — Read and write pcap files with `rdpcap()` / `wrpcap()`
41
41
  - **Python Bindings** — Seamless integration via PyO3/maturin
42
42
  - **Custom Protocols** — Define runtime protocols with `CustomLayer` and typed fields
@@ -327,6 +327,45 @@ config = FlowConfig(
327
327
  conversations = extract_flows("capture.pcap", config=config)
328
328
  ```
329
329
 
330
+ Optional: Track maximum packet sizes during flow extraction:
331
+
332
+ ```python
333
+ config = FlowConfig(
334
+ track_max_packet_len=True, # Track max per-direction (forward_max_packet_len, reverse_max_packet_len)
335
+ track_max_flow_len=True, # Track overall max (max_flow_len)
336
+ )
337
+ conversations = extract_flows("capture.pcap", config=config)
338
+
339
+ for conv in conversations:
340
+ print(f"Max fwd packet: {conv.forward_max_packet_len} bytes")
341
+ print(f"Max rev packet: {conv.reverse_max_packet_len} bytes")
342
+ print(f"Max overall: {conv.max_flow_len} bytes")
343
+ ```
344
+
345
+ Disabled by default (zero overhead). Enable only when needed for flow analysis.
346
+
347
+ #### ICMP and ICMPv6 Flow Tracking
348
+
349
+ Automatically correlate ICMP echo request/reply pairs and track other ICMP message types:
350
+
351
+ ```python
352
+ conversations = extract_flows("capture.pcap")
353
+
354
+ for conv in conversations:
355
+ if conv.protocol == "ICMP" or conv.protocol == "ICMPv6":
356
+ print(f"ICMP Echo: {conv.src_addr} <-> {conv.dst_addr}")
357
+ print(f" Type: {conv.icmp_type}, Code: {conv.icmp_code}")
358
+ print(f" Identifier: {conv.icmp_identifier}")
359
+ print(f" Requests: {conv.icmp_request_count}, Replies: {conv.icmp_reply_count}")
360
+ print(f" Last seq: {conv.icmp_last_seq}")
361
+ ```
362
+
363
+ Features:
364
+ - Echo request/reply pairs correlated via identifier (symmetric src/dst ports)
365
+ - Non-echo message types tracked via (type, code) substitution
366
+ - Properties: `icmp_type`, `icmp_code`, `icmp_identifier`, `icmp_request_count`, `icmp_reply_count`, `icmp_last_seq`
367
+ - Returns `None` for non-ICMP flows
368
+
330
369
  ## Rust Crate
331
370
 
332
371
  The core library is available as a standalone Rust crate:
@@ -11,8 +11,8 @@
11
11
 
12
12
  - **Scapy-style API** — Stack layers with `Ether() / IP() / TCP()`, set fields with keyword arguments
13
13
  - **High Performance** — Core logic in Rust, zero-copy parsing, copy-on-write mutation
14
- - **Broad Protocol Support** — Ethernet, ARP, IPv4/IPv6, TCP, UDP, ICMP, ICMPv6, DNS, HTTP/1.x, HTTP/2, QUIC, L2TP, 802.11 (Wi-Fi), 802.15.4 (Zigbee), and custom protocols
15
- - **Stateful Flow Extraction** — Extract bidirectional conversations from PCAP files with TCP state tracking, stream reassembly, and UDP timeout handling
14
+ - **Broad Protocol Support** — Ethernet, ARP, IPv4/IPv6, TCP, UDP, ICMP/ICMPv6 (with echo correlation), DNS, HTTP/1.x, HTTP/2, QUIC, L2TP, 802.11 (Wi-Fi), 802.15.4 (Zigbee), and custom protocols
15
+ - **Stateful Flow Extraction** — Extract bidirectional conversations from PCAP files with TCP state tracking, stream reassembly, UDP timeout handling, and optional max packet/flow length tracking
16
16
  - **PCAP I/O** — Read and write pcap files with `rdpcap()` / `wrpcap()`
17
17
  - **Python Bindings** — Seamless integration via PyO3/maturin
18
18
  - **Custom Protocols** — Define runtime protocols with `CustomLayer` and typed fields
@@ -303,6 +303,45 @@ config = FlowConfig(
303
303
  conversations = extract_flows("capture.pcap", config=config)
304
304
  ```
305
305
 
306
+ Optional: Track maximum packet sizes during flow extraction:
307
+
308
+ ```python
309
+ config = FlowConfig(
310
+ track_max_packet_len=True, # Track max per-direction (forward_max_packet_len, reverse_max_packet_len)
311
+ track_max_flow_len=True, # Track overall max (max_flow_len)
312
+ )
313
+ conversations = extract_flows("capture.pcap", config=config)
314
+
315
+ for conv in conversations:
316
+ print(f"Max fwd packet: {conv.forward_max_packet_len} bytes")
317
+ print(f"Max rev packet: {conv.reverse_max_packet_len} bytes")
318
+ print(f"Max overall: {conv.max_flow_len} bytes")
319
+ ```
320
+
321
+ Disabled by default (zero overhead). Enable only when needed for flow analysis.
322
+
323
+ #### ICMP and ICMPv6 Flow Tracking
324
+
325
+ Automatically correlate ICMP echo request/reply pairs and track other ICMP message types:
326
+
327
+ ```python
328
+ conversations = extract_flows("capture.pcap")
329
+
330
+ for conv in conversations:
331
+ if conv.protocol == "ICMP" or conv.protocol == "ICMPv6":
332
+ print(f"ICMP Echo: {conv.src_addr} <-> {conv.dst_addr}")
333
+ print(f" Type: {conv.icmp_type}, Code: {conv.icmp_code}")
334
+ print(f" Identifier: {conv.icmp_identifier}")
335
+ print(f" Requests: {conv.icmp_request_count}, Replies: {conv.icmp_reply_count}")
336
+ print(f" Last seq: {conv.icmp_last_seq}")
337
+ ```
338
+
339
+ Features:
340
+ - Echo request/reply pairs correlated via identifier (symmetric src/dst ports)
341
+ - Non-echo message types tracked via (type, code) substitution
342
+ - Properties: `icmp_type`, `icmp_code`, `icmp_identifier`, `icmp_request_count`, `icmp_reply_count`, `icmp_last_seq`
343
+ - Returns `None` for non-ICMP flows
344
+
306
345
  ## Rust Crate
307
346
 
308
347
  The core library is available as a standalone Rust crate:
@@ -20,6 +20,10 @@ pub struct FlowConfig {
20
20
  pub max_ooo_fragments: usize,
21
21
  /// Interval between idle conversation eviction sweeps (default: 30s).
22
22
  pub eviction_interval: Duration,
23
+ /// Track maximum packet length per direction (default: false).
24
+ pub track_max_packet_len: bool,
25
+ /// Track maximum flow length per direction (default: false).
26
+ pub track_max_flow_len: bool,
23
27
  }
24
28
 
25
29
  impl Default for FlowConfig {
@@ -32,6 +36,8 @@ impl Default for FlowConfig {
32
36
  max_reassembly_buffer: 16 * 1024 * 1024, // 16 MB
33
37
  max_ooo_fragments: 100,
34
38
  eviction_interval: Duration::from_secs(30),
39
+ track_max_packet_len: false,
40
+ track_max_flow_len: false,
35
41
  }
36
42
  }
37
43
  }
@@ -50,5 +56,7 @@ mod tests {
50
56
  assert_eq!(config.max_reassembly_buffer, 16 * 1024 * 1024);
51
57
  assert_eq!(config.max_ooo_fragments, 100);
52
58
  assert_eq!(config.eviction_interval, Duration::from_secs(30));
59
+ assert!(!config.track_max_packet_len);
60
+ assert!(!config.track_max_flow_len);
53
61
  }
54
62
  }
@@ -0,0 +1,144 @@
1
+ use std::time::Duration;
2
+
3
+ use crate::Packet;
4
+
5
+ use super::config::FlowConfig;
6
+ use super::state::ConversationStatus;
7
+
8
+ /// ICMP/ICMPv6 conversation state.
9
+ ///
10
+ /// Tracks ICMP-specific metadata for echo request/reply pairs and other ICMP types.
11
+ /// Echo requests and replies are correlated using the ICMP identifier field.
12
+ #[derive(Debug, Clone)]
13
+ pub struct IcmpFlowState {
14
+ /// ICMP type (e.g., 8 for Echo Request, 0 for Echo Reply).
15
+ pub icmp_type: u8,
16
+ /// ICMP code.
17
+ pub icmp_code: u8,
18
+ /// ICMP identifier (for echo, timestamp, and other types that use it).
19
+ pub identifier: Option<u16>,
20
+ /// Number of echo requests (type 8 for ICMP, 128 for ICMPv6).
21
+ pub request_count: u64,
22
+ /// Number of echo replies (type 0 for ICMP, 129 for ICMPv6).
23
+ pub reply_count: u64,
24
+ /// Last sequence number seen in an echo packet.
25
+ pub last_seq: Option<u16>,
26
+ /// Conversation status.
27
+ pub status: ConversationStatus,
28
+ }
29
+
30
+ impl IcmpFlowState {
31
+ #[must_use]
32
+ pub fn new(icmp_type: u8, icmp_code: u8) -> Self {
33
+ Self {
34
+ icmp_type,
35
+ icmp_code,
36
+ identifier: None,
37
+ request_count: 0,
38
+ reply_count: 0,
39
+ last_seq: None,
40
+ status: ConversationStatus::Active,
41
+ }
42
+ }
43
+
44
+ /// Update state when a new ICMP packet is received.
45
+ ///
46
+ /// Increments request or reply count based on ICMP type, and updates
47
+ /// the identifier and sequence number fields if present.
48
+ pub fn process_packet(&mut self, packet: &Packet, buf: &[u8], icmp_type: u8, icmp_code: u8) {
49
+ // Update type/code on every packet (they should be consistent)
50
+ self.icmp_type = icmp_type;
51
+ self.icmp_code = icmp_code;
52
+
53
+ // Get ICMP layer bounds to extract fields
54
+ if let Some(icmp_layer) = crate::layer::LayerKind::Icmp
55
+ .try_into()
56
+ .ok()
57
+ .and_then(|kind| packet.get_layer(kind))
58
+ {
59
+ let icmp_start = icmp_layer.start;
60
+
61
+ // Extract identifier (bytes 4-5) if present
62
+ if buf.len() >= icmp_start + 6 {
63
+ self.identifier = Some(u16::from_be_bytes([
64
+ buf[icmp_start + 4],
65
+ buf[icmp_start + 5],
66
+ ]));
67
+ }
68
+
69
+ // Extract sequence number (bytes 6-7) if present
70
+ if buf.len() >= icmp_start + 8 {
71
+ self.last_seq = Some(u16::from_be_bytes([
72
+ buf[icmp_start + 6],
73
+ buf[icmp_start + 7],
74
+ ]));
75
+ }
76
+
77
+ // Count requests and replies based on ICMP type
78
+ match icmp_type {
79
+ 8 => {
80
+ // ICMP Echo Request
81
+ self.request_count += 1;
82
+ },
83
+ 0 => {
84
+ // ICMP Echo Reply
85
+ self.reply_count += 1;
86
+ },
87
+ 128 => {
88
+ // ICMPv6 Echo Request
89
+ self.request_count += 1;
90
+ },
91
+ 129 => {
92
+ // ICMPv6 Echo Reply
93
+ self.reply_count += 1;
94
+ },
95
+ _ => {
96
+ // Other ICMP types: no counting
97
+ },
98
+ }
99
+ }
100
+
101
+ self.status = ConversationStatus::Active;
102
+ }
103
+
104
+ /// Check whether this flow has timed out.
105
+ #[must_use]
106
+ pub fn check_timeout(&self, last_seen: Duration, now: Duration, config: &FlowConfig) -> bool {
107
+ // ICMP uses UDP timeout
108
+ now.saturating_sub(last_seen) > config.udp_timeout
109
+ }
110
+ }
111
+
112
+ impl Default for IcmpFlowState {
113
+ fn default() -> Self {
114
+ Self::new(0, 0)
115
+ }
116
+ }
117
+
118
+ #[cfg(test)]
119
+ mod tests {
120
+ use super::*;
121
+
122
+ #[test]
123
+ fn test_icmp_state_new() {
124
+ let state = IcmpFlowState::new(8, 0);
125
+ assert_eq!(state.icmp_type, 8);
126
+ assert_eq!(state.icmp_code, 0);
127
+ assert_eq!(state.request_count, 0);
128
+ assert_eq!(state.reply_count, 0);
129
+ assert_eq!(state.identifier, None);
130
+ assert_eq!(state.last_seq, None);
131
+ }
132
+
133
+ #[test]
134
+ fn test_icmp_timeout() {
135
+ let config = FlowConfig::default(); // 120s UDP timeout
136
+ let state = IcmpFlowState::new(8, 0);
137
+
138
+ // Not timed out
139
+ assert!(!state.check_timeout(Duration::from_secs(100), Duration::from_secs(200), &config));
140
+
141
+ // Timed out
142
+ assert!(state.check_timeout(Duration::from_secs(100), Duration::from_secs(300), &config));
143
+ }
144
+ }
@@ -291,7 +291,59 @@ pub fn extract_key(packet: &Packet) -> Result<(CanonicalKey, FlowDirection), Flo
291
291
  .map_err(|e| FlowError::PacketError(e.into()))?;
292
292
  (sport, dport)
293
293
  },
294
- // ICMP and other protocols have no ports
294
+ TransportProtocol::Icmp => {
295
+ // For ICMP, use identifier (for echo/timestamp types) for both ports
296
+ // (symmetric), or type+code as port substitute for other types.
297
+ // Using identifier symmetrically ensures request and reply have
298
+ // the same canonical key regardless of direction.
299
+ if let Some(icmp_layer) = packet.get_layer(LayerKind::Icmp) {
300
+ if buf.len() >= icmp_layer.start + 8 {
301
+ let icmp_type = buf[icmp_layer.start];
302
+ let is_echo = icmp_type == 0 || icmp_type == 8;
303
+ if is_echo {
304
+ let id = u16::from_be_bytes([
305
+ buf[icmp_layer.start + 4],
306
+ buf[icmp_layer.start + 5],
307
+ ]);
308
+ (id, id) // Use identifier symmetrically for both ports
309
+ } else {
310
+ let code = buf[icmp_layer.start + 1];
311
+ (icmp_type as u16, code as u16)
312
+ }
313
+ } else {
314
+ (0u16, 0u16)
315
+ }
316
+ } else {
317
+ (0u16, 0u16)
318
+ }
319
+ },
320
+ TransportProtocol::Icmpv6 => {
321
+ // For ICMPv6, use identifier (for echo/timestamp types) for both ports
322
+ // (symmetric), or type+code as port substitute for other types.
323
+ // Using identifier symmetrically ensures request and reply have
324
+ // the same canonical key regardless of direction.
325
+ if let Some(icmpv6_layer) = packet.get_layer(LayerKind::Icmpv6) {
326
+ if buf.len() >= icmpv6_layer.start + 8 {
327
+ let icmpv6_type = buf[icmpv6_layer.start];
328
+ let is_echo = icmpv6_type == 128 || icmpv6_type == 129;
329
+ if is_echo {
330
+ let id = u16::from_be_bytes([
331
+ buf[icmpv6_layer.start + 4],
332
+ buf[icmpv6_layer.start + 5],
333
+ ]);
334
+ (id, id) // Use identifier symmetrically for both ports
335
+ } else {
336
+ let code = buf[icmpv6_layer.start + 1];
337
+ (icmpv6_type as u16, code as u16)
338
+ }
339
+ } else {
340
+ (0u16, 0u16)
341
+ }
342
+ } else {
343
+ (0u16, 0u16)
344
+ }
345
+ },
346
+ // Other protocols have no ports
295
347
  _ => (0u16, 0u16),
296
348
  };
297
349
 
@@ -27,6 +27,7 @@
27
27
 
28
28
  pub mod config;
29
29
  pub mod error;
30
+ pub mod icmp_state;
30
31
  pub mod key;
31
32
  pub mod state;
32
33
  pub mod table;
@@ -37,6 +38,7 @@ pub mod udp_state;
37
38
  // Re-exports
38
39
  pub use config::FlowConfig;
39
40
  pub use error::FlowError;
41
+ pub use icmp_state::IcmpFlowState;
40
42
  pub use key::{
41
43
  CanonicalKey, FlowDirection, TransportProtocol, ZWaveKey, extract_key, extract_zwave_key,
42
44
  };
@@ -119,7 +121,7 @@ pub fn extract_zwave_flows(
119
121
  state
120
122
  });
121
123
 
122
- conv.record_packet(direction, byte_count, timestamp, index);
124
+ conv.record_packet(direction, byte_count, timestamp, index, false, false);
123
125
 
124
126
  // Track ACK vs command frames
125
127
  if let ProtocolState::ZWave(ref mut zw) = conv.protocol_state
@@ -1,6 +1,7 @@
1
1
  use std::time::Duration;
2
2
 
3
3
  use super::config::FlowConfig;
4
+ use super::icmp_state::IcmpFlowState;
4
5
  use super::key::{CanonicalKey, FlowDirection, TransportProtocol};
5
6
  use super::tcp_state::TcpConversationState;
6
7
  use super::udp_state::UdpFlowState;
@@ -47,6 +48,8 @@ pub struct DirectionStats {
47
48
  pub first_seen: Duration,
48
49
  /// Timestamp of the most recent packet in this direction.
49
50
  pub last_seen: Duration,
51
+ /// Maximum packet length in this direction (if tracking enabled).
52
+ pub max_packet_len: Option<u64>,
50
53
  }
51
54
 
52
55
  impl DirectionStats {
@@ -57,14 +60,18 @@ impl DirectionStats {
57
60
  bytes: 0,
58
61
  first_seen: timestamp,
59
62
  last_seen: timestamp,
63
+ max_packet_len: None,
60
64
  }
61
65
  }
62
66
 
63
67
  /// Record a new packet in this direction.
64
- pub fn record_packet(&mut self, byte_count: u64, timestamp: Duration) {
68
+ pub fn record_packet(&mut self, byte_count: u64, timestamp: Duration, track_max_len: bool) {
65
69
  self.packets += 1;
66
70
  self.bytes += byte_count;
67
71
  self.last_seen = timestamp;
72
+ if track_max_len {
73
+ self.max_packet_len = Some(self.max_packet_len.unwrap_or(0).max(byte_count));
74
+ }
68
75
  }
69
76
  }
70
77
 
@@ -75,9 +82,13 @@ pub enum ProtocolState {
75
82
  Tcp(TcpConversationState),
76
83
  /// UDP pseudo-conversation with timeout tracking.
77
84
  Udp(UdpFlowState),
85
+ /// ICMP conversation with echo request/reply tracking.
86
+ Icmp(IcmpFlowState),
87
+ /// ICMPv6 conversation with echo request/reply tracking.
88
+ Icmpv6(IcmpFlowState),
78
89
  /// Z-Wave wireless conversation with home ID and node tracking.
79
90
  ZWave(ZWaveFlowState),
80
- /// Other protocols (ICMP, etc.) — no specific state tracking.
91
+ /// Other protocols — no specific state tracking.
81
92
  Other,
82
93
  }
83
94
 
@@ -115,6 +126,8 @@ pub struct ConversationState {
115
126
  pub packet_indices: Vec<usize>,
116
127
  /// Protocol-specific state.
117
128
  pub protocol_state: ProtocolState,
129
+ /// Maximum packet length across both directions (if tracking enabled).
130
+ pub max_flow_len: Option<u64>,
118
131
  }
119
132
 
120
133
  impl ConversationState {
@@ -124,6 +137,8 @@ impl ConversationState {
124
137
  let protocol_state = match key.protocol {
125
138
  TransportProtocol::Tcp => ProtocolState::Tcp(TcpConversationState::new()),
126
139
  TransportProtocol::Udp => ProtocolState::Udp(UdpFlowState::new()),
140
+ TransportProtocol::Icmp => ProtocolState::Icmp(IcmpFlowState::new(0, 0)),
141
+ TransportProtocol::Icmpv6 => ProtocolState::Icmpv6(IcmpFlowState::new(0, 0)),
127
142
  _ => ProtocolState::Other,
128
143
  };
129
144
 
@@ -136,6 +151,7 @@ impl ConversationState {
136
151
  reverse: DirectionStats::new(timestamp),
137
152
  packet_indices: Vec::new(),
138
153
  protocol_state,
154
+ max_flow_len: None,
139
155
  }
140
156
  }
141
157
 
@@ -172,6 +188,7 @@ impl ConversationState {
172
188
  command_count: 0,
173
189
  ack_count: 0,
174
190
  }),
191
+ max_flow_len: None,
175
192
  }
176
193
  }
177
194
 
@@ -200,13 +217,26 @@ impl ConversationState {
200
217
  byte_count: u64,
201
218
  timestamp: Duration,
202
219
  packet_index: usize,
220
+ track_max_packet_len: bool,
221
+ track_max_flow_len: bool,
203
222
  ) {
204
223
  self.last_seen = timestamp;
205
224
  self.packet_indices.push(packet_index);
206
225
 
207
226
  match direction {
208
- FlowDirection::Forward => self.forward.record_packet(byte_count, timestamp),
209
- FlowDirection::Reverse => self.reverse.record_packet(byte_count, timestamp),
227
+ FlowDirection::Forward => {
228
+ self.forward
229
+ .record_packet(byte_count, timestamp, track_max_packet_len);
230
+ },
231
+ FlowDirection::Reverse => {
232
+ self.reverse
233
+ .record_packet(byte_count, timestamp, track_max_packet_len);
234
+ },
235
+ }
236
+
237
+ // Update max flow length if tracking is enabled
238
+ if track_max_flow_len {
239
+ self.max_flow_len = Some(self.max_flow_len.unwrap_or(0).max(byte_count));
210
240
  }
211
241
  }
212
242
 
@@ -230,6 +260,12 @@ impl ConversationState {
230
260
  ProtocolState::Udp(udp) => {
231
261
  self.status = udp.status;
232
262
  },
263
+ ProtocolState::Icmp(icmp) => {
264
+ self.status = icmp.status;
265
+ },
266
+ ProtocolState::Icmpv6(icmpv6) => {
267
+ self.status = icmpv6.status;
268
+ },
233
269
  ProtocolState::ZWave(_) => {},
234
270
  ProtocolState::Other => {},
235
271
  }
@@ -250,6 +286,7 @@ impl ConversationState {
250
286
  }
251
287
  },
252
288
  ProtocolState::Udp(_) => elapsed > config.udp_timeout,
289
+ ProtocolState::Icmp(_) | ProtocolState::Icmpv6(_) => elapsed > config.udp_timeout,
253
290
  ProtocolState::ZWave(_) => elapsed > config.udp_timeout,
254
291
  ProtocolState::Other => elapsed > config.udp_timeout,
255
292
  }
@@ -72,7 +72,14 @@ impl ConversationTable {
72
72
  let conv = entry.value_mut();
73
73
 
74
74
  // Record packet stats
75
- conv.record_packet(direction, byte_count, timestamp, packet_index);
75
+ conv.record_packet(
76
+ direction,
77
+ byte_count,
78
+ timestamp,
79
+ packet_index,
80
+ self.config.track_max_packet_len,
81
+ self.config.track_max_flow_len,
82
+ );
76
83
 
77
84
  // Process protocol-specific state
78
85
  let buf = packet.as_bytes();
@@ -85,6 +92,26 @@ impl ConversationTable {
85
92
  ProtocolState::Udp(udp_state) => {
86
93
  udp_state.process_packet();
87
94
  },
95
+ ProtocolState::Icmp(icmp_state) => {
96
+ // Get ICMP type and code from buffer
97
+ if let Some(icmp_layer) = packet.get_layer(crate::layer::LayerKind::Icmp) {
98
+ if buf.len() >= icmp_layer.start + 2 {
99
+ let icmp_type = buf[icmp_layer.start];
100
+ let icmp_code = buf[icmp_layer.start + 1];
101
+ icmp_state.process_packet(packet, buf, icmp_type, icmp_code);
102
+ }
103
+ }
104
+ },
105
+ ProtocolState::Icmpv6(icmpv6_state) => {
106
+ // Get ICMPv6 type and code from buffer
107
+ if let Some(icmpv6_layer) = packet.get_layer(crate::layer::LayerKind::Icmpv6) {
108
+ if buf.len() >= icmpv6_layer.start + 2 {
109
+ let icmpv6_type = buf[icmpv6_layer.start];
110
+ let icmpv6_code = buf[icmpv6_layer.start + 1];
111
+ icmpv6_state.process_packet(packet, buf, icmpv6_type, icmpv6_code);
112
+ }
113
+ }
114
+ },
88
115
  ProtocolState::ZWave(_) => {},
89
116
  ProtocolState::Other => {},
90
117
  }