Rule with flow option "only_stream" never seems to match anything

Hey everyone,

I am trying to understand what exactly the “only_stream” option of the flow keywords exactly does. From the documentation of the flow keyword:

Match on packets that have been reassembled by the stream engine.

To me this sounds like this should match when the TCP frames of the trace need to be reassembled. After looking into the source code (following the flags for only_stream and looking more closely into stream-tcp-reassemble.c) I believe this even more since the match counter should get increased if the option is set and the StreamPayload returns that some reassembly was done in the tcp-reassembly class.

However: when I use the following rule to match any reassembled frames, no alerts are ever returned.
alert ip any any -> any any (flow:only_stream; sid:1;)

The traces I used should require some reassembly at least (out of order TCP frames, ack-ing unseen segments, etc.), however the rule above returns no matches (and when I use no_stream instead every frame matches). Is there anything I’m missing why this rule never matches? Or can someone please point me to an example where this does indeed return any alerts at all?

Suricata.yaml stream engine settings I am using, in case I need to align something here (please note: my traces are rather small, < 1MB, but I would assume TCP reassembly should also happen in those cases?):

stream:
  memcap: 64mb
  checksum-validation: yes      # reject wrong csums
  inline: auto                  # auto will use inline mode in IPS mode, yes or no set it statically
  reassembly:
    memcap: 256mb
    depth: 1mb                  # reassemble 1mb into a stream
    toserver-chunk-size: 2560
    toclient-chunk-size: 2560
    randomize-chunk-size: yes
    #randomize-chunk-range: 10
    #raw: yes
    #segment-prealloc: 2048
    #check-overlap-different-data: true

Hi. Just wanted to chime in and say that I get no matches with some pcaps I had laying around.
Some other rules with flow:established did trigger. I always assumed that flow:only_stream meant only matching on the reassembled buffer with the payloads from the complete flow, and not matching on the payloads of each individual packet.

The following rule did trigger (a lot): alert ip any any -> any any (msg:"stream"; flow:only_stream; content:" "; sid:1;)

Which kind of makes sense if the flag only affects which buffers are used for content matching, and no content matching is used.

Wow, you are right. When I use your rule with the added content, then I actually get matches as well.

Yes, it seems like that is actually the case for the only_stream flag. However I still think it’s at least not really consistent behaviour then. If the flag affects which buffers are used, then why does no_stream work differently? So currently the matching results look something like this for me:

flow:only_stream; --> no matches ever
flow:no_stream; --> matches all frames
flow:only_stream; content:" "; --> more matches than first rule
flow:no_stream; content:" "; --> less matches then second rule

which makes sense since you obviously can not get more then all / less than none matches. I guess no_stream does not affect the buffer which is used, therefore we still match on the individual packets and everything matches even without content matching. Still, it’s definitely not very intuitive to me, but thanks for the explanation / experimentation with the rules.