Skip to main content
Inspectors are the main workhorse between packet decoding and IPS detection. They correspond to Snort 2’s preprocessors and cover everything from flow tracking and TCP reassembly to application-layer protocol analysis.

Inspector types

The InspectorType enum controls when and how an inspector is invoked:
// framework/inspector.h
enum InspectorType
{
    IT_PASSIVE,     // config only, or data consumer (eg file_log, binder, ftp_client)
    IT_PACKET,      // processes raw packets only (eg normalize, capture)
    IT_STREAM,      // flow tracking and reassembly (eg ip, tcp, udp)
    IT_NETWORK,     // process packets w/o service (eg arp, bo)
    IT_SERVICE,     // extract and analyze service PDUs (eg dce, http, ssl)
    IT_CONTROL,     // process all packets before detection (eg appid)
    IT_PROBE,       // process all packets after detection (eg perf_monitor, port_scan)
    IT_PROBE_FIRST, // process all packets before detection (eg packet_capture)
    IT_MAX
};
Runs on every packet before IPS detection. AppID is always first among control inspectors.
Processes packets at the raw layer, before reassembly. Used for normalization and capture.
Manages flow state, IP defragmentation, and TCP reassembly. Also handles files and TCP payload-only streams directly.
Handles protocols that don’t ride on a recognized service (e.g. ARP spoofing, Back Orifice).
Parses application-layer PDUs: HTTP, FTP, SSL, DCE/RPC, Telnet, etc.
For configuration-only plugins, or for consuming inspection events published by other inspectors (binder, ftp_client, file_log).
Runs after all detection, on every packet. Used for performance monitoring and port scanning.
Similar to IT_CONTROL but specifically for capture-style inspectors like packet_capture that need access to all packets before the detection engine runs.
The wizard inspector uses IT_SERVICE type in the plugin API, even though its logical role is service identification. The IT_WIZARD label described in user documentation is a conceptual role, not a separate enum value.

The Inspector class

All inspectors inherit from snort::Inspector:
// framework/inspector.h
class SO_PUBLIC Inspector
{
public:
    virtual ~Inspector();

    // --- Main thread ---

    // Validate config and resolve external dependencies.
    // Return false to signal a fatal configuration error.
    virtual bool configure(SnortConfig*) { return true; }

    // Called for inspectors removed during a live reload.
    // shutdown==true when Snort is exiting entirely.
    virtual void tear_down(SnortConfig*, bool /*shutdown*/) { }

    // Return true if this inspector has nothing to do given the config;
    // Snort will skip it entirely.
    virtual bool disable(SnortConfig*) { return false; }

    // Print current configuration to the console.
    virtual void show(const SnortConfig*) const { }

    // --- Packet thread ---

    // One-time thread-local initialization.
    virtual void tinit() { }
    // Thread-local cleanup.
    virtual void tterm() { }

    // Return true if this inspector wants to see this packet.
    // Default filter is based on the api proto / paf setting.
    virtual bool likes(Packet*);

    // Analyze the packet.
    virtual void eval(Packet*) { }

    // Release thread-local or flow-based data after eval.
    virtual void clear(Packet*) { }

    // --- Buffer access ---

    // Well-known buffer access (IBT_VBA, IBT_JS_DATA, etc.)
    virtual bool get_buf(InspectionBuffer::Type, Packet*, InspectionBuffer&)
    { return false; }

    // Generic named buffer access (key listed in InspectApi::buffers)
    virtual bool get_buf(const char* key, Packet*, InspectionBuffer&);
    virtual bool get_buf(unsigned id, Packet*, InspectionBuffer&)
    { return false; }

    // IT_SERVICE only: provide a StreamSplitter for PDU segmentation.
    virtual class StreamSplitter* get_splitter(bool to_server);

protected:
    Inspector();
};

Defining the Module

An inspector’s configuration parameters are handled by a companion Module. The following is the pattern used in doc/devel/extending.txt:
#include "framework/module.h"

#define GADGET_NAME "gadget"
#define GADGET_HELP "example inspector"

// Parameters accepted by this inspector in snort.lua
static const Parameter gadget_params[] =
{
    { "brain", Parameter::PT_BOOL, nullptr, "true", "enable brain" },
    { "claw",  Parameter::PT_INT,  "0:10",  "3",    "claw count"  },
    { nullptr, Parameter::PT_MAX,  nullptr, nullptr, nullptr       }
};

struct GadgetConfig
{
    bool brain;
    int  claw;
};

class GadgetModule : public Module
{
public:
    GadgetModule() : Module(GADGET_NAME, GADGET_HELP, gadget_params) { }

    bool set(const char*, Value& v, SnortConfig*) override
    {
        if ( v.is("brain") )     config.brain = v.get_bool();
        else if ( v.is("claw") ) config.claw  = v.get_long();
        return true;
    }

    // Hand ownership of config to the inspector constructor.
    GadgetConfig* get_config() { return new GadgetConfig(config); }

private:
    GadgetConfig config {};
};
Configured in snort.lua as:
gadget =
{
    brain = true,
    claw = 3
}

Implementing the Inspector

#include "framework/inspector.h"

class GadgetInspector : public Inspector
{
public:
    GadgetInspector(GadgetConfig* c) : config(c) { }
    ~GadgetInspector() { delete config; }

    bool configure(SnortConfig*) override
    {
        // Resolve dependencies on other inspectors here.
        return true;
    }

    void show(const SnortConfig*) const override
    {
        // Print current configuration.
        ConfigLogger::log_flag("brain", config->brain);
        ConfigLogger::log_value("claw", config->claw);
    }

    void eval(Packet* p) override
    {
        // Inspect the packet.
    }

private:
    GadgetConfig* config;
};

The InspectApi structure

The InspectApi struct wires together your Module, Inspector class, and lifecycle callbacks:
// framework/inspector.h
struct InspectApi
{
    BaseApi base;          // common plugin header
    InspectorType type;    // IT_* constant
    uint32_t proto_bits;   // PROTO_BIT_* mask of protocols this inspector handles

    const char** buffers;  // null-terminated list of named buffers this inspector exports
    const char* service;   // service name (only when type == IT_SERVICE)

    InspectFunc pinit;     // plugin-level init (once at startup)
    InspectFunc pterm;     // plugin-level cleanup
    InspectFunc tinit;     // thread-local init
    InspectFunc tterm;     // thread-local cleanup
    InspectNew  ctor;      // construct Inspector from Module
    InspectDelFunc dtor;   // destroy Inspector
    InspectSsnFunc ssn;    // return a new session tracker (IT_STREAM only)
    InspectFunc reset;     // reset stats
};
A minimal registration:
static Inspector* gadget_ctor(Module* m)
{
    GadgetModule* gm = static_cast<GadgetModule*>(m);
    return new GadgetInspector(gm->get_config());
}

static void gadget_dtor(Inspector* p)
{ delete p; }

static const InspectApi gadget_api =
{
    {
        PT_INSPECTOR,
        sizeof(InspectApi),
        INSAPI_VERSION,
        0,           // plugin version
        0,           // features
        nullptr,     // options
        GADGET_NAME,
        GADGET_HELP,
        []() -> Module* { return new GadgetModule; }, // mod_ctor
        [](Module* m) { delete m; },                  // mod_dtor
    },
    IT_NETWORK,  // inspector type
    0,           // proto_bits
    nullptr,     // buffers
    nullptr,     // service
    nullptr,     // pinit
    nullptr,     // pterm
    nullptr,     // tinit
    nullptr,     // tterm
    gadget_ctor,
    gadget_dtor,
    nullptr,     // ssn
    nullptr,     // reset
};

SO_PUBLIC const BaseApi* snort_plugins[] =
{
    &gadget_api.base,
    nullptr
};

Trace Logger inspectors

Trace Loggers print diagnostic trace messages and are implemented as inspector plugins. The pattern requires three classes: a logger, a factory, and an inspector.
1

Implement TraceLogger

Inherit from TraceLogger and override log():
class FooLogger : public TraceLogger
{
public:
    void log(const char*, const char*, uint8_t, const char*, const Packet*) override
    { printf("%s%s\n", "Foo", "Bar"); }
};
2

Implement TraceLoggerFactory

The factory creates logger instances on demand:
class FooFactory : public TraceLoggerFactory
{
public:
    TraceLogger* instantiate() override
    { return new FooLogger(); }
};
3

Register the factory in configure()

The inspector’s configure() method installs the factory via TraceApi:
bool FooInspector::configure(SnortConfig* sc) override
{
    return TraceApi::override_logger_factory(sc, new FooFactory());
}
4

Register as a plugin

Export through snort_plugins[] exactly like any other inspector, using the appropriate InspectApi.
Inspector::configure() runs on the main thread. The factory must be installed before any packet threads start processing.

Key design rules

Plugins must keep configuration (parse-time) separate from runtime state. Store runtime state in FlowData on the flow, or in thread-local storage. Never store mutable per-packet state in configuration objects.
  • eval(), clear(), and likes() run on packet threads — they must be thread-safe.
  • configure(), show(), and tear_down() run on the main thread.
  • tinit() / tterm() run on each packet thread once at startup/shutdown.