The order of packet inspection in suricata is strange

TCP 10 packet :
login01
login02
login03
login04
login05
logout01
logout02
logout03
logout04
logout05

Rule exampleA:
alert tcp any any → any any (msg:“sig01”; sid:11; rev:3; content:“login01”; nocase; dsize:8; flowint:foo,notset; flowint:foo,=,1; noalert;)
alert tcp any any → any any (msg:“sig02”; sid:12; rev:3; content:“logout01”; nocase; dsize:9; flowint:foo,+,3; noalert; )
alert tcp any any → any any (msg:“sig03”; sid:13; rev:3; content:“logout05”; nocase; flowint:foo,>=,4; )

Rule exampleB:
alert tcp any any → any any (msg:“sig01”; sid:11; rev:3; content:“login01”; nocase; flowint:foo,notset; flowint:foo,=,1; noalert;)
alert tcp any any → any any (msg:“sig02”; sid:12; rev:3; content:“logout01”; nocase; flowint:foo,+,3; noalert; )
alert tcp any any → any any (msg:“sig03”; sid:13; rev:3; content:“logout05”; nocase; flowint:foo,>=,4; )

Example A and Example B have different packet inspection order.
ExampleA:
18/7/2023 – 09:56:13 - - Our var foo is at idx: 1
18/7/2023 – 09:56:13 - - Not set foo? = 1
18/7/2023 – 09:56:13 - - Our var foo is at idx: 1
18/7/2023 – 09:56:13 - - Setting foo = 1
18/7/2023 – 09:56:13 - - Our var foo is at idx: 1
18/7/2023 – 09:56:13 - - Adding 3 to foo
18/7/2023 – 09:56:13 - - respect_inspect_depth false
18/7/2023 – 09:56:13 - - respect_inspect_depth false
18/7/2023 – 09:56:13 - - Our var foo is at idx: 1
18/7/2023 – 09:56:13 - - ( 4 GE 4 )

ExampleB:
18/7/2023 – 09:57:09 - - Our var foo is at idx: 1
18/7/2023 – 09:57:09 - - Adding 3 to foo (new var)
18/7/2023 – 09:57:09 - - respect_inspect_depth false
18/7/2023 – 09:57:09 - - Our var foo is at idx: 1
18/7/2023 – 09:57:09 - - Not set foo? = 0
18/7/2023 – 09:57:09 - - respect_inspect_depth false
18/7/2023 – 09:57:09 - - Our var foo is at idx: 1
18/7/2023 – 09:57:09 - - ( 3 GE 4 )

I don’t know why the two examples differ in the check order
Any advice would be appreciated.

Suricata processes packets using one or more worker threads. Packets are distributed to the worker threads based on a couple of parameters, the most important of which are the runmode and the packet source

Your examples show something but we’re not sure what created the output.

To better help we’d need

  • Suricata version in use
  • Whether you build Suricata yourself or not
  • Suricata configuration file
  • Command line used to launch Suricata
  • Output of suricata --build-info

Thanks

my environment is
Suricata 6.0.10
Build the downloaded source code
I use default configure (/usr/local/etc/suricata/suricata.yaml)
I hardly modified the config file

/usr/local/bin/suricata -c /usr/local/etc/suricata/suricata.yaml -k none -r pkt.pcap

This is Suricata version 6.0.10 RELEASE
Features: DEBUG PCAP_SET_BUFF AF_PACKET HAVE_PACKET_FANOUT LIBCAP_NG LIBNET1.1 HAVE_HTP_URI_NORMALIZE_HOOK PCRE_JIT
SIMD support: SSE_4_2 SSE_4_1 SSE_3
Atomic intrinsics: 1 2 4 8 16 byte(s)
64-bits, Little-endian architecture
GCC version 7.5.0, C version 201112
compiled with _FORTIFY_SOURCE=2
L1 cache line size (CLS)=64
thread local storage method: _Thread_local
compiled with LibHTP v0.5.42, linked against LibHTP v0.5.42

Suricata Configuration:
AF_PACKET support: yes
eBPF support: no
XDP support: no
PF_RING support: no
NFQueue support: no
NFLOG support: no
IPFW support: no
Netmap support: no using new api: no
DAG enabled: no
Napatech enabled: no
WinDivert enabled: no

Unix socket enabled: yes
Detection enabled: yes

Libmagic support: no
libnss support: no
libnspr support: no
libjansson support: yes
hiredis support: no
hiredis async with libevent: no
Prelude support: no
PCRE jit: yes
LUA support: no
libluajit: no
GeoIP2 support: no
Non-bundled htp: no
Hyperscan support: no
Libnet support: yes
liblz4 support: no
HTTP2 decompression: no

Rust support: yes
Rust strict mode: no
Rust compiler path: /usr/bin/rustc
Rust compiler version: rustc 1.65.0
Cargo path: /usr/bin/cargo
Cargo version: cargo 1.65.0
Cargo vendor: yes
Python support: yes
Python path: /usr/bin/python3
Install suricatactl: yes
Install suricatasc: yes
Install suricata-update: yes

Profiling enabled: no
Profiling locks enabled: no

Plugin support (experimental): yes

Development settings:
Coccinelle / spatch: no
Unit tests enabled: no
Debug output enabled: yes
Debug validation enabled: no

Generic build parameters:
Installation prefix: /usr/local
Configuration directory: /usr/local/etc/suricata/
Log directory: /usr/local/var/log/suricata/

–prefix /usr/local
–sysconfdir /usr/local/etc
–localstatedir /usr/local/var
–datarootdir /usr/local/share

Host: x86_64-pc-linux-gnu
Compiler: gcc (exec name) / g++ (real)
GCC Protect enabled: no
GCC march native enabled: yes
GCC Profile enabled: no
Position Independent Executable enabled: no
CFLAGS -g -O2 -std=c11 -march=native -I${srcdir}/…/rust/gen -I${srcdir}/…/rus
PCAP_CFLAGS -I/usr/include
SECCFLAGS

Thanks for supplying the additional information.

Suricata’s flow handling may seem out of order but it’s “ok”. I’ll explain a bit more

  • Suricata’s multithreaded
  • There are many worker threads; each worker thread receives flows for processing
  • Flows are sent to worker threads based on a hash of the 5 tuple. This means that there’s “flow affinity” so a given flow will be processed by the same worker thread
  • Threads operate concurrently

If you want more deterministic flow packet processing ordering, try adding --runmode=single to the command line. This causes all packet processing to be done by a single worker thread and eliminates concurrent packet processing.