Overview

Before Snort 2.9, Snort called functions from the libpcap library directly. In other words, calls to PCAP library functions like pcap_dispatch()
pcap_dispatch() reads packets from an interface or PCAP file and calls a function (the "callback" function) for each of these packets to "process" the packet. In the case of Snort, "processing" the packet typically means looking for something suspicious.
were made by the Snort code in Snort versions prior to Snort 2.9. In Snort 2.9, the "Data Acquisition Library" (DAQ) was introduced. DAQ "replaces direct calls to libpcap functions with an abstraction layer that facilitates operation on a variety of hardware and software interfaces without requiring changes to Snort."
DAQ became necessary due to the shortcomings of the PCAP library - the most important of which was the inability to handle inline (i.e., Intrusion Protection System - IPS) mode
While the PCAP library can inject packets onto an interface, the PCAP library can't read packets from one interface and inject packets onto another.
. The afpacket DAQ module, on the other hand, can. So instead of calling PCAP- or afpacket-specific functions (e.g., pcap_dispatch) directly from the Snort code, generic functions (e.g., daq_acquire_with_meta()) from the DAQ library are called from the Snort code. These in turn call the function of a specific module
I discuss the DAQ PCAP module more than the other DAQ modules simply because it's the default DAQ module and is used more frequently than the other DAQ modules.
Note that these are "modules" in the traditional - more generic - sense (i.e., compiled code that has a specific purpose) and not related to the Module-derived classes that are used for the purpose of configuration.
. daq_acquire_with_meta() is the function that Snort++ calls to "acquire" (i.e., grab) packets from an interface or PCAP file. If the DAQ PCAP (daq_pcap) module is used, daq_acquire_with_meta() calls pcap_daq_acquire(), which in turn calls pcap_dispatch() from the PCAP library (libpcap.so). If another DAQ module is used, daq_acquire_with_meta() calls the appropriate function (e.g., afpacket_daq_acquire(), ipfw_daq_acquire()) instead.
name:

website:

comment:


Static vs Dynamic Linking of DAQ Library / Dynamic Linking of PCAP Library

The DAQ libraries can be either statically linked to the Snort executable (the default) or dynamically linked to the Snort executable. Here is a diagram showing the call trace of pcap_acquire() of two of the modules within DAQ (DAQ-pcap and DAQ-afpacket) when DAQ is statically linked to the Snort executable
To "statically link" a library means that the relevant library code is copied into the Snort executable. With a dynamically linked (shared object) library, the functions are stored in a separate file, the "shared object" .so file (in Windows-speak, the "DLL").
.
Here's the case where DAQ is not statically linked to the Snort executable.
As you can see from the diagrams above, the PCAP library is never statically linked to the Snort executable. The PCAP library will always be a separate file, regardless whether DAQ is compiled external to the Snort executable. On the other hand, there is no external afpacket dynamic library that is called by afpacket_daq_acquire() and the other DAQ afpacket functions. All of the code necessary for Snort to use afpacket resides within the DAQ library.
So to recap - there are two libraries: the DAQ library and the PCAP library. The DAQ library can either be a DLL or be statically linked into the Snort executable. The PCAP library, on the other hand, will always be a DLL
The PCAP library is a common library available to virtually every OS and is used by many applications. The afpacket code within the DAQ library, on the other hand, was written by the Snort developers and (as far as I know) is not used by any application besides Snort. Since no other application is dependent on the afpacket code, the entire afpacket code might as well be statically linked into the Snort executable rather than having a separate afpacket DLL. This is not true for the PCAP code. The core PCAP code (i.e., the code that actually does the work of reading and injecting packets onto an interface) cannot be statically linked into the Snort executable. If the PCAP module is used, calls to the PCAP DLL library, libpcap.so, are made.
. As mentioned above, statically linking the DAQ code into Snort++ is not the only option. Snort++ can be configured to not statically link the DAQ library and instead access the DAQ functions in the DAQ dynamic library. During the Snort++ build, the --disable-static-daq option for the configure script is used
In my opinion, there don't seem to be many advantages to using the DAQ dynamic libraries instead of the DAQ static libraries.
.
name:

website:

comment:


SFDAQInstance

DAQ is initialized globally and per interface
Snort++ is multi-threaded which allows each interface to have its own processor for its pipeline.
. A single DAQ module (e.g., daq_pcap) must be used for all threads and is specified on the command line or in the configuration file. The existence of the specified DAQ module must of course be verified as well as the ability of the module to handle certain modes
For example, daq_pcap can't handle DAQ_MODE_LINE (which is inline/IPS mode), as I described above.
. The most important thing that the DAQ interface initialization does it to make sure that the network interface (e.g., "eth0") or PCAP file exists.
The SFDAQInstance class is used to control DAQ for a specific interface. Once DAQ has been initialized, the very important call to SFDAQInstance::acquire(), which specifies the very important callback function (which will - for IDS mode - be packet_callback()) is made. SFDAQInstance::acquire() calls daq_acquire_with_meta() which (as described above) calls the specific function (e.g., pcap_daq_acquire()) appropriate for a given module (e.g., daq_pcap). After SFDAQInstance::acquire() is called, the callback function will be called for each packet received
How often acquire() should return (to allow for tasks other than packet processing) is a source of discussion.
.
Each DAQ module must implement (i.e., provide) the fields and functions of DAQ_Module_t. We'll discuss DAQ_Module_t next.
name:

website:

comment:


DAQ_Module_t

There are six possible DAQ modules to choose from. They are daq_afpacket, daq_dump, daq_ipfw, daq_ipq, daq_nfq, and daq_pcap
On my Ubuntu system, only daq_afpacket, daq_dump, daq_ipfw, and daq_pcap were built by default. daq_ipq and daq_nfq were not built. This was due to the absence of some installed packages and their header files. For example, daq_nfq was not built due to the absence of the libnefilter_queue-dev package and its libnetfilter_queue/libnetfilter_queue.h header file.
. As mentioned above, every DAQ module must implement the fields and functions of DAQ_Module_t
In programmer-speak, DAQ_Module_t is the "Application Programming Interface" (i.e., API) to DAQ.
. This is where the specific function (e.g., pcap_daq_acquire()) for a given module (e.g., daq_pcap) is specified.
typedef struct _daq_module { /* The version of the API this module implements. This *must* be the first element in the structure. */ const uint32_t api_version; /* The version of the DAQ module itself - can be completely arbitrary. */ const uint32_t module_version; /* The name of the module (sfpacket, xvnim, pcap, etc.) */ const char *name; /* Various flags describing the module and its capabilities (Inline-capabale, etc.) */ const uint32_t type; /* Initialize the device for packet acquisition with the supplied configuration. This should not start queuing packets for the application. */ int (*initialize) (const DAQ_Config_t *config, void **ctxt_ptr, char *errbuf, size_t len); /* Set the module's BPF based on the given string */ int (*set_filter) (void *handle, const char *filter); /* Complete device opening and begin queuing packets if they have not been already. */ int (*start) (void *handle); /* Acquire up to packets and call for each with as the final argument. The return value of the callback will determine the action taken by the DAQ for each packet. If is 0, packets will continue to be acquired until some other factor breaks the acquisition loop. */ int (*acquire) (void *handle, int cnt, DAQ_Analysis_Func_t callback, DAQ_Meta_Func_t metaback, void *user); /* Inject a new packet going either the same or opposite direction as the specified packet. */ int (*inject) (void *handle, const DAQ_PktHdr_t *hdr, const uint8_t *packet_data, uint32_t len, int reverse); /* Force breaking out of the acquisition loop after the current iteration. */ int (*breakloop) (void *handle); /* Stop queuing packets, if possible */ int (*stop) (void *handle); /* Close the device and clean up */ void (*shutdown) (void *handle); ... int (*hup_prep) (void *handle, void **new_config); /* Swap new and old configuration */ int (*hup_apply) (void *handle, void *new_config, void **old_config); /* Destroy old configuration */ int (*hup_post) (void *handle, void *old_config); } DAQ_Module_t;
For example, here is daq_pcap's implementation:
#ifdef BUILDING_SO DAQ_SO_PUBLIC const DAQ_Module_t DAQ_MODULE_DATA = #else const DAQ_Module_t pcap_daq_module_data = #endif { .api_version = DAQ_API_VERSION, .module_version = DAQ_PCAP_VERSION, .name = "pcap", .type = DAQ_TYPE_FILE_CAPABLE | DAQ_TYPE_INTF_CAPABLE | DAQ_TYPE_MULTI_INSTANCE, .initialize = pcap_daq_initialize, .set_filter = pcap_daq_set_filter, .start = pcap_daq_start, .acquire = pcap_daq_acquire, .inject = pcap_daq_inject, .breakloop = pcap_daq_breakloop, .stop = pcap_daq_stop, ... .get_device_index = pcap_daq_get_device_index, .modify_flow = NULL, .hup_prep = NULL, .hup_apply = NULL, .hup_post = NULL, #else ...
Many of the fields in the DAQ_Module_t struct are pointers to functions. These functions must be implemented by the module. For example, the field acquire is a pointer to a function and is implemented by the pcap_daq_acquire() function for daq_pcap.
name:

website:

comment:


DAQ Initialization

Setting up and invoking DAQ requires several steps. These steps can be seen in a bare-bones program written by Christos Karayiannis. The calls to DAQ (e.g., daq_initialize(), daq_start()) are handled by objects of the SFDAQInstance class in the Snort++ code.
name:

website:

comment: