In Suricata’s official document, there are oversimplified descriptions for these three protocol options, and it’s really confusing.
tcp
(for tcp-traffic)tcp-pkt
(for matching content in individual tcp packets)tcp-stream
(for matching content only in a reassembled tcp stream)
In order to figure out the difference between these three options, I did some experiments and tried to summarize some patterns.
tcp
(for tcp traffic)- It looks like Suricata performs both individual packet detection (
tcp-pkt
) and stream detection (tcp-stream
). - Under normal circumstances, Suricata will output only the stream detection result if both detections produce results and overlap. If only one type of detection produces a result, it will output that result accordingly.
- Like tcp-stream, it may
- However, in certain “unexpected scenarios,” Suricata may output both individual packet detection and stream detection results, even if they overlap in the same stream. I’ve conducted some experiments but haven’t identified a clear pattern for this behavior.
- This is based on my experiments. The official documentation does not explicitly explain, and the real answer might only be found by analyzing Suricata’s source code. (´・_・`)
- It looks like Suricata performs both individual packet detection (
tcp-pkt
(for matching content in individual tcp packets)- If you want the packet number matched by a Suricata rule (
content: "xxx"
) to be the same as the packet filtered by the Wireshark (tcp contains "xxx"
), then you should selecttcp-pkt
instead of the other two.
- If you want the packet number matched by a Suricata rule (
tcp-stream
(for matching content only in a reassembled tcp stream)- Theoretically, Suricata performs matching in the stream after TCP reassembly. However, it doesn’t necessarily reconstruct the entire TCP stream. The extent of reassembly depends on Suricata’s reassembly configuration and the length of the current TCP stream (It may still be active).
- So, if you analyze an offline pcap traffic file using
tcp
ortcp-stream
options, Suricata may output an inaccurate packet number (pcap_cnt
) that differs from the exact TCP packet filtered in Wireshark. It may be theFIN
packet of the stream or just a subsequence packet in the stream. However, they indeed belong to the same TCP stream.
Experiment
I got a pcap file: tcp-ethereal-trace-1, and the traffic has an HTTP stream.

I wrote these four rules to match the string ‘POST /et‘ at the No. 4 packet shown in Wireshark.
alert tcp-pkt any any -> 128.119.245.12 80 (msg:"funway - String Check"; flow:established,to_server; content:"POST /et"; classtype:string-detect; sid:1000001; rev:1;)
alert tcp-stream any any -> 128.119.245.12 80 (msg:"funway - String Check"; flow:established,to_server; content:"POST /et"; classtype:string-detect; sid:1000002; rev:1;)
alert tcp any any -> 128.119.245.12 80 (msg:"funway - String Check"; flow:established,to_server; content:"POST /et"; classtype:string-detect; sid:1000003; rev:1;)
alert tcp any any -> 128.119.245.12 80 (msg:"funway - String Check"; flow:established,to_server; content:"POST /et"; depth:10; classtype:string-detect; sid:1000004; rev:1;)
The results are shown below:

- Rule 1
tcp-pkt
matches one packet, which is the same as the No. 4 packet in Wireshark. - Rule 2
tcp-stream
matches one packet, which is a subsequence packet in the TCP stream. - Rule 3
tcp
outputs only one result, same withtcp-stream
. - Rule 4
tcp
withdepth
option outputs two results, like a combination oftcp-pkt
andtcp-stream
. W…eird.
Then, I tried to use these three options to match a string that is split into two packets.

The results are shown below, tcp-pkt
matches nothing.
