Rule Processing
===============

.. toctree::

Suricata rules have many elements that influence how they are processed by
Suricata and matched against network traffic.

This section explains some key aspects of how Suricata handles rules internally,
so it can be easier to understand/predict how different rules may interact in
specific scenarios.

**This material is intended for:** rule writers; developers.

Possible questions one should be better equipped to answer after reading this
document:

- What happens if two rules have the same priority value (the keyword)?
- What type of rules will be evaluated first, given a set of rules?
- How does Suricata decide what rule is "more important" when matching traffic?

.. important:: Rules processing is also heavily affected by rule types, as mentioned
   in this chapter. You may want to read more on :doc:`rule-types`.

.. note::

    Throughout this documentation and for Suricata, the terms "rule" and
    "signature" are mainly interchangeable, unless context indicates otherwise.

Overview
--------

Rules are provided to Suricata via rules files. Starting from those, the
Detection Engine loader will:

#. Load all the signatures;
#. Check for validity (non-existing keywords; duplicated ``sid`` etc);
#. Report stats on loaded, good and bad signatures;
#. Sort all valid signatures and store them in a list in the Detect Engine
   Context (``DetectEngineCtx``) structure, taking into consideration several
   rule aspects according to their order of relevance to the Detection Engine;
#. Attribute internal rule IDs that reflect this `rule prioritization`_;
#. During inspection, match rules against the inspected traffic, according to rule
   and traffic type.

.. _rule prioritization:

Rule Prioritization
-------------------

Suricata registers several different ordering functions (with
``SCSigRegisterSignatureOrderingFuncs()``), which are then used to compare
the rules, sort them, and define their priority. The elements taken into
consideration for such are the signature's:

#. :ref:`Action <actions>`
#. Usage of :ref:`flowbits`
#. Usage of :ref:`flowint`
#. Usage of flowvar
#. Usage of pktvar
#. Usage of hostbits
#. Usage of ippair
#. :ref:`"Priority" keyword<priority>`

In this order. Once signatures are ordered, they are attributed a unique
internal ID (``Signature::iid``) which symbolizes their priority (the lower the
``iid``, the higher the priority). This could mean that a rule with a
keyword-defined priority of 1 could have lower priority than another rule that
had flowbits set and a rule action with higher priority, for instance.

.. note:: this list isn't fully comprehensive, in the sense that each item has
   extra logic for prioritization. For example, considering flowbits, the
   priority is write (highest) > write + read > read (lowest) > no flowbits.

Another important element when considering rule parsing, processing and matching
is that the ruleset is optimized into signature group heads based on the signature
elements (thus, for instance, a TCP rule and an UDP rule would be loaded into
different groups, and their internal ids will not interfere between one another,
as they're matched against different traffic). For more on this, see
:ref:`detection-engine`.

Inspection Process
------------------

Once it is time to inspect network traffic against the loaded rules, the
Detect Engine will match against - if applicable:

#. IP Only rules;
#. Packet/payload-related rules;
#. Frame keywords;
#. Application layer protocol transaction rules.

During packet inspection, if the signature uses the last two in this list,
inspection is left to those steps.

.. tip::

  With the introduction of :doc:`Firewall mode <../firewall/firewall-design>`,
  it is possible to explicitly control to which step of the detection engine flow
  a rule will be hooked. This is done with :ref:`Explicit rule hooks <rule-hooks>`.

For each rule that is matched, a ``PacketAlert`` is created. After all matches
for a packet have been processed, and the :ref:`alert queue limit <alert queue
overflow impact>` is taken into account, the remaining ``PacketAlerts`` become
the alerts in Suricata logs.

Considerations on Inspection Steps
----------------------------------

IP Only rules
~~~~~~~~~~~~~

Without optimization, IP Only signatures would match on every packet on a flow.
To improve performance, what Suricata does is to evaluate rules that are
``ip-only`` only once per flow direction, for the first packet in each direction.

Application layer protocol transactions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Each parser has its own state machine, and uses a per-direction parsing state
"progress". Keywords can be registered for each progress value. So, for
instance, ``http`` has a value "request line available" for which there are
keywords like :ref:`http.uri <rules-http-uri-normalization>`, :ref:`http.method
<http.method>` etc. registered. While parsing the traffic, if the engine reaches
this state, the signatures with those keywords may be already evaluated, even if
they have a lower priority than an http body inspecting signature.

Relatedly, a rule with two keywords matching at two different progress stages may
be evaluated against two different packets.

Implications
------------

Action precedence and interaction with ``ip-only`` rules
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To illustrate what may be counter-intuitive implications of how inspection
steps, action prioritization and rule keywords interact and affect the engine
behavior, we will use a real case example. Consider these three rules:

.. container:: example-rule

    pass tcp 0.0.0.0/0 any <> 0.0.0.0/0 443 (msg:"Allow TCP in port 443"; flow:
    not_established; sid:1; rev:1;)

.. container:: example-rule

    pass tcp 0.0.0.0/0 any <> 0.0.0.0/0 80 (msg:"Allow TCP in port 80"; flow:
    not_established; sid:2; rev:1;)

.. container:: example-rule

    drop ip 0.0.0.0/0 any -> 0.0.0.0/0 any (msg:"No outbound internet access
    from host"; sid:3; rev:1;)

The first two are signatures that analyze individual packets and match only if
the flow has not been established (``flow:not_established``): the rules grant
``PASS`` to the matched packet - but not to its flow.

The third signature is considered ``ip-only``. This means it will be evaluated
for the *first* packet in both directions of a flow, in addition to rules 1 and
2. By extension, **the other packets in the same flow will not be evaluated
against this rule.**

With an action order configuration that prioritizes ``PASS`` over ``DROP``, this
means that rules 1 and 2 will have a higher internal priority over rule 3,
therefore nullifying the ``DROP`` outcome. The result: a flow for outbound
internet traffic from the host, expected to be dropped, wouldn't be.

If the expected behavior with those three signatures was to allow traffic on
ports 80 and 443 only, while dropping everything else, the simplest way to
achieve this would be to remove the ``flow:not_established`` portion from rules
sid:1 and sid:2. This ensures that the ``PASS`` action would be applied to the
whole flow following the match on the first packet and that all other traffic
would be dropped.

Following that, all three rules will be evaluated on the same step, and if a
flow isn't flagged with ``pass``, it will be dropped with the third rule.

.. Tip::
   A more straightforward way to achieve that in Suricata 8 is using the firewall
   more. See :doc:`../firewall/firewall-design`.

Alerts not seen
~~~~~~~~~~~~~~~

Another aspect of rule prioritization combined with the alerts queue size is
that, in corner case scenarios, if a packet matches against too many rules,
signatures with lower priority could be discarded from the ``PacketAlert`` queue
(see the section on :ref:`alert queue overflow impact <alert queue overflow impact>`
for more).

The stats counter ``detect.alert_queue_overflow`` will be higher than zero if
an alert was discarded due to ``Alert Queue`` overflow (cf. :ref:`alerts stats`).

