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
. 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.
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()
, 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()
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.
The SFDAQInstance class
is used to control DAQ for a specific interface. Once DAQ has been initialized, the very important call
, which specifies the very important callback function (which will - for IDS mode - be packet_callback()
) is made. SFDAQInstance::acquire()
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.
There are six possible DAQ modules to choose from. They are daq_afpacket
, 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);
For example, here is daq_pcap's implementation:
DAQ_SO_PUBLIC const DAQ_Module_t DAQ_MODULE_DATA =
const DAQ_Module_t pcap_daq_module_data =
.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,
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.