Skip to main content
In Snort 2, network and port-based configuration was spread across multiple places: policy bindings by VLAN or network, per-preprocessor port lists, and a global ignore-ports list. In Snort 3, all of that is handled in a single module called the binder.

What the binder does

The binder is an ordered list of when/use rules. When a session starts — and again if the service on that session is later identified — Snort walks the list top to bottom and applies the first matching binding for network criteria and the first matching binding for service criteria. binder.when specifies the matching criteria. binder.use specifies what to do: allow the traffic, load a config file, or assign an inspector.
binder =
{
    { when = { ... }, use = { ... } },
    { when = { ... }, use = { ... } },
    ...
}

Binding criteria (when)

FieldTypeDescription
protostringLayer 4 protocol: tcp, udp, ip, icmp
portsstringSpace-separated port list or range, e.g. '80 8080' or '1024:'
netsstringCIDR network, e.g. '192.168.0.0/16'
vlansstringVLAN ID(s)
ifacestringNetwork interface name
servicestringService name identified by the wizard or appid
rolestring'server' or 'client' — constrains which side the port applies to
Any combination of these fields may be used. All specified criteria must match.

Binding actions (use)

FieldDescription
action'allow', 'block', or 'inspect'
filePath to a Lua config file to apply to matching sessions
typeName of an inspector module to assign
nameInstance name of the inspector (for non-default instances)

The full default binder

The snort.lua default configuration ships with a comprehensive binder:
binder =
{
    -- Port bindings for protocols without wizard support
    { when = { proto = 'udp', ports = '53',          role = 'server' }, use = { type = 'dns' } },
    { when = { proto = 'tcp', ports = '53',          role = 'server' }, use = { type = 'dns' } },
    { when = { proto = 'tcp', ports = '111',         role = 'server' }, use = { type = 'rpc_decode' } },
    { when = { proto = 'tcp', ports = '502',         role = 'server' }, use = { type = 'modbus' } },
    { when = { proto = 'tcp', ports = '2123 2152 3386', role = 'server' }, use = { type = 'gtp_inspect' } },
    { when = { proto = 'tcp', ports = '2404',        role = 'server' }, use = { type = 'iec104' } },
    { when = { proto = 'udp', ports = '2222',        role = 'server' }, use = { type = 'cip' } },
    { when = { proto = 'tcp', ports = '4840',        role = 'server' }, use = { type = 'opcua' } },
    { when = { proto = 'tcp', ports = '44818',       role = 'server' }, use = { type = 'cip' } },

    -- DCE/RPC transport bindings
    { when = { proto = 'tcp', service = 'dcerpc' },  use = { type = 'dce_tcp' } },
    { when = { proto = 'udp', service = 'dcerpc' },  use = { type = 'dce_udp' } },
    { when = { proto = 'udp', service = 'netflow' }, use = { type = 'netflow' } },

    -- Service bindings (matched after wizard or appid identifies the service)
    { when = { service = 'netbios-ssn' },      use = { type = 'dce_smb' } },
    { when = { service = 'dce_http_server' },  use = { type = 'dce_http_server' } },
    { when = { service = 'dce_http_proxy' },   use = { type = 'dce_http_proxy' } },
    { when = { service = 'cip' },              use = { type = 'cip' } },
    { when = { service = 'dnp3' },             use = { type = 'dnp3' } },
    { when = { service = 'dns' },              use = { type = 'dns' } },
    { when = { service = 'ftp' },              use = { type = 'ftp_server' } },
    { when = { service = 'ftp-data' },         use = { type = 'ftp_data' } },
    { when = { service = 'gtp' },              use = { type = 'gtp_inspect' } },
    { when = { service = 'http' },             use = { type = 'http_inspect' } },
    { when = { service = 'http2' },            use = { type = 'http2_inspect' } },
    { when = { service = 'iec104' },           use = { type = 'iec104' } },
    { when = { service = 'imap' },             use = { type = 'imap' } },
    { when = { service = 'mms' },              use = { type = 'mms' } },
    { when = { service = 'modbus' },           use = { type = 'modbus' } },
    { when = { service = 'opcua' },            use = { type = 'opcua' } },
    { when = { service = 'pop3' },             use = { type = 'pop' } },
    { when = { service = 's7commplus' },       use = { type = 's7commplus' } },
    { when = { service = 'sip' },              use = { type = 'sip' } },
    { when = { service = 'smtp' },             use = { type = 'smtp' } },
    { when = { service = 'socks' },            use = { type = 'socks' } },
    { when = { service = 'ssh' },              use = { type = 'ssh' } },
    { when = { service = 'ssl' },              use = { type = 'ssl' } },
    { when = { service = 'sunrpc' },           use = { type = 'rpc_decode' } },
    { when = { service = 'telnet' },           use = { type = 'telnet' } },

    -- Fallback: hand unidentified sessions to the wizard for autodetection
    { use = { type = 'wizard' } }
}

Common binding patterns

Equivalent to Snort 2’s config ignore_ports:
{ when = { proto = 'tcp', ports = '22' }, use = { action = 'allow' } }
Equivalent to Snort 2’s config binding by vlan:
{ when = { vlans = '1024' }, use = { file = 'vlan.lua' } }
Equivalent to a targeted preprocessor config in Snort 2:
-- Route port-8080 HTTP traffic through a separately configured inspector
{ when = { nets = '192.168.0.0/16', proto = 'tcp', ports = '8080' },
  use = { name = 'alt_http', type = 'http_inspect' } }
The balanced.lua tweak inserts an explicit HTTP port binding at position 1 for performance:
table.insert(
    binder, 1,
    { when = { proto = 'tcp', ports = '80', role = 'server' }, use = { type = 'http_inspect' } }
)

The wizard and autodetection

The wizard is an inspector that identifies services by inspecting the initial payload of a session, without relying on port numbers. It is the catch-all binding at the bottom of the binder list:
{ use = { type = 'wizard' } }   -- matches any session not matched above
The wizard uses three types of patterns defined in snort_defaults.lua:
Pattern typeHow it worksExample protocols
SpellsCase-insensitive text patterns with glob wildcardsHTTP, SMTP, FTP, SSH, SIP
HexesHexadecimal byte patterns with single-byte wildcards (?)DNP3, SSL/TLS, HTTP/2, Telnet
CursesInternal C++ state-machine algorithmsDCE/RPC (UDP/TCP/SMB), MMS, OPCUA, SSLv2
When the wizard identifies a service, the binder list is re-evaluated from the top so the correct inspector can be assigned for the rest of the session.
-- Example spells in default_wizard:
{ service = 'http',  proto = 'tcp', to_server = { 'GET', 'POST', 'HEAD', '...' }, to_client = { 'HTTP/' } },
{ service = 'smtp',  proto = 'tcp', to_server = { 'HELO', 'EHLO' }, to_client = { '220*SMTP', '220*MAIL' } },
{ service = 'ssh',   proto = 'tcp', to_server = { 'SSH-' }, to_client = { 'SSH-' } },
Port bindings at the top of the binder list take priority over wizard identification. Use explicit port bindings for protocols like DNS (port 53) and Modbus (port 502) that do not have reliable text signatures.

Default binder

If the binder is not explicitly configured in any Lua file or --lua option, Snort instantiates a default binder that creates service bindings for all configured service inspectors. Some of those bindings may require the wizard to detect the service type.
# Inspect the binder module parameters
snort --help-module binder
Unlike Snort 2, where bindings could only be configured in the default policy, each Snort 3 policy can contain its own binder. This allows an arbitrary hierarchy of policies.

Build docs developers (and LLMs) love