From: Michael Altizer Date: Fri, 31 Aug 2018 18:40:41 +0000 (-0400) Subject: DAQng: Port Snort and its DAQ modules to DAQ3 X-Git-Tag: 3.0.0-256~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=42f72b3882882bac943b11774d4f794e4a7cc8e6;p=thirdparty%2Fsnort3.git DAQng: Port Snort and its DAQ modules to DAQ3 - Massive refactoring of the Analyzer thread - Handle multiple offloaded wire packets - Port hext and file DAQ modules to DAQng - Reimplement the RETRY verdict internal to Snort - Revamp skip-n/exit-after-n/pause-after-n handling - Update lua tweaks with new DAQ configuration format - Update sfdaq unit tests for DAQng - Update snort2lua to convert to new DAQ configuration --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ba889448..5be70a626 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,6 +93,14 @@ Compiler options: Feature options:\ ") +if (ENABLE_STATIC_DAQ) + message("\ + DAQ Modules: Static (${DAQ_STATIC_MODULES})") +else () + message("\ + DAQ Modules: Dynamic") +endif () + if (HAVE_FLATBUFFERS) message("\ Flatbuffers: ON") diff --git a/cmake/FindDAQ.cmake b/cmake/FindDAQ.cmake index 7ff9df97d..357037c14 100644 --- a/cmake/FindDAQ.cmake +++ b/cmake/FindDAQ.cmake @@ -1,65 +1,70 @@ -# -# Locate DAQ library -# This module defines -# DAQ_FOUND, if false, do not try to link to Lua -# -# DAQ_FOUND - system has libdaq -# DAQ_INCLUDE_DIR - the libdaq include directory -# DAQ_LIBRARIES - the libraries needed to use libdaq -# +#[=======================================================================[.rst: +FindDAQ +------- -set (ERROR_MESSAGE - " - ERROR! Cannot find LibDAQ. Go get it from - http://snort.org/snort-downloads or use the --with-daq-* - options if you have it installed in an unusual place.\n\n" -) +Locate LibDAQ include paths and library as well as static DAQ module libraries -find_path(DAQ_INCLUDE_DIR - NAMES daq.h - HINTS ${DAQ_INCLUDE_DIR_HINT} - NO_SYSTEM_ENVIRONMENT_PATH -) +This module defines: -# find any static libraries -if (ENABLE_STATIC_DAQ) - execute_process( - COMMAND daq-modules-config --static --libs - OUTPUT_VARIABLE DAQ_STATIC_LIBRARIES - RESULT_VARIABLE result - OUTPUT_STRIP_TRAILING_WHITESPACE - ) +:: - # This will be false if the exit status was 0 and true if the binary was not found - if (result) - message(SEND_ERROR - " - ERROR! Cannot find LibDAQ's static libraries! Make sure the binary - file `daq-modules-config` is in your path.\n\n") - endif() + DAQ_FOUND - system has libdaq + DAQ_INCLUDE_DIR - the libdaq include directory + DAQ_LIBRARIES - the libraries needed to use libdaq + DAQ_STATIC_MODULES - the static DAQ modules available + DAQ_STATIC_MODULE_LIBS - the set of additional libraries required by the available static DAQ modules +#]=======================================================================] - set(DAQ_LIB daq_static) -else() - set(DAQ_LIB daq) - set(DAQ_STATIC_LIBRARIES) -endif() +find_package(PkgConfig) +pkg_check_modules(PC_DAQ libdaq>=3.0.0) -find_library(DAQ_LIBRARY - NAMES ${DAQ_LIB} - HINTS ${DAQ_LIBRARIES_DIR_HINT} # user-specified path in ./configure_cmake.sh - DOC "DAQ library directory" +# Use DAQ_INCLUDE_DIR_HINT and DAQ_LIBRARIES_DIR_HINT from configure_cmake.sh as primary hints +# and then package config information after that. +find_path(DAQ_INCLUDE_DIR + NAMES daq.h + HINTS ${DAQ_INCLUDE_DIR_HINT} ${PC_DAQ_INCLUDEDIR} ${PC_DAQ_INCLUDE_DIRS} + NO_SYSTEM_ENVIRONMENT_PATH +) +find_library(DAQ_LIBRARIES + NAMES daq + HINTS ${DAQ_LIBRARIES_DIR_HINT} ${PC_DAQ_LIBDIR} ${PC_DAQ_LIBRARY_DIRS} ) - -set(DAQ_LIBRARIES ${DAQ_LIBRARY} ${DAQ_STATIC_LIBRARIES}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(DAQ - REQUIRED_VARS DAQ_LIBRARY DAQ_LIBRARIES DAQ_INCLUDE_DIR + REQUIRED_VARS DAQ_LIBRARIES DAQ_INCLUDE_DIR FAIL_MESSAGE "${ERROR_MESSAGE}" ) mark_as_advanced( DAQ_INCLUDE_DIR - DAQ_LIBRARY DAQ_LIBRARIES ) + +if (PKG_CONFIG_EXECUTABLE AND ENABLE_STATIC_DAQ) + execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --list-all + OUTPUT_VARIABLE _pkgconfig_list_result + OUTPUT_STRIP_TRAILING_WHITESPACE) + string(REGEX MATCHALL "libdaq_static_[^ ]+" AVAILABLE_STATIC_MODULES ${_pkgconfig_list_result}) + list(REMOVE_DUPLICATES AVAILABLE_STATIC_MODULES) + foreach (STATIC_MODULE IN LISTS AVAILABLE_STATIC_MODULES) + string(REGEX REPLACE "^libdaq_static_" "" MODULE_NAME ${STATIC_MODULE}) + list(APPEND DAQ_STATIC_MODULES ${MODULE_NAME}) + pkg_check_modules(PC_${STATIC_MODULE} ${STATIC_MODULE}) + foreach (STATIC_MODULE_LIBNAME IN LISTS PC_${STATIC_MODULE}_LIBRARIES) + find_library(STATIC_MODULE_LIB + NAMES ${STATIC_MODULE_LIBNAME} + HINTS ${PC_${STATIC_MODULE}_LIBRARY_DIRS}) + if (STATIC_MODULE_LIB) + list(APPEND DAQ_STATIC_MODULE_LIBS ${STATIC_MODULE_LIB}) + unset(STATIC_MODULE_LIB CACHE) + endif() + endforeach() + endforeach() + if (DAQ_STATIC_MODULE_LIBS) + list(REMOVE_DUPLICATES DAQ_STATIC_MODULE_LIBS) + endif() + if (DAQ_STATIC_MODULES) + list(SORT DAQ_STATIC_MODULES) + endif() +endif() diff --git a/daqs/daq_file.c b/daqs/daq_file.c index 6fe158653..dbbd3ec5a 100644 --- a/daqs/daq_file.c +++ b/daqs/daq_file.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -33,308 +34,359 @@ #include #include -#include -#include +#include #define DAQ_MOD_VERSION 0 #define DAQ_NAME "file" #define DAQ_TYPE (DAQ_TYPE_FILE_CAPABLE|DAQ_TYPE_INTF_CAPABLE|DAQ_TYPE_MULTI_INSTANCE) + +#define FILE_DEFAULT_POOL_SIZE 16 #define FILE_BUF_SZ 16384 -typedef struct { - char* name; - int fid; +#define SET_ERROR(modinst, ...) daq_base_api.set_errbuf(modinst, __VA_ARGS__) + +typedef struct _file_msg_desc +{ + DAQ_Msg_t msg; + DAQ_PktHdr_t pkthdr; + DAQ_UsrHdr_t pci; + uint8_t* data; + struct _file_msg_desc* next; +} FileMsgDesc; - int start; - int stop; - int eof; +typedef struct +{ + FileMsgDesc* pool; + FileMsgDesc* freelist; + DAQ_MsgPoolInfo_t info; +} FileMsgPool; +typedef struct +{ + /* Configuration */ + char* filename; unsigned snaplen; - uint8_t* buf; - char error[DAQ_ERRBUF_SIZE]; + /* State */ + DAQ_ModuleInstance_h modinst; + FileMsgPool pool; + int fid; + volatile bool interrupted; + + bool sof; + bool eof; DAQ_UsrHdr_t pci; - DAQ_State state; DAQ_Stats_t stats; -} FileImpl; +} FileContext; + +static DAQ_BaseAPI_t daq_base_api; + +//------------------------------------------------------------------------- +// utility functions +//------------------------------------------------------------------------- + +static void destroy_message_pool(FileContext* fc) +{ + FileMsgPool* pool = &fc->pool; + if (pool->pool) + { + while (pool->info.size > 0) + free(pool->pool[--pool->info.size].data); + free(pool->pool); + pool->pool = NULL; + } + pool->freelist = NULL; + pool->info.available = 0; + pool->info.mem_size = 0; +} + +static int create_message_pool(FileContext* fc, unsigned size) +{ + FileMsgPool* pool = &fc->pool; + pool->pool = calloc(sizeof(FileMsgDesc), size); + if (!pool->pool) + { + SET_ERROR(fc->modinst, "%s: Could not allocate %zu bytes for a packet descriptor pool!", + __func__, sizeof(FileMsgDesc) * size); + return DAQ_ERROR_NOMEM; + } + pool->info.mem_size = sizeof(FileMsgDesc) * size; + while (pool->info.size < size) + { + /* Allocate packet data and set up descriptor */ + FileMsgDesc *desc = &pool->pool[pool->info.size]; + desc->data = malloc(fc->snaplen); + if (!desc->data) + { + SET_ERROR(fc->modinst, "%s: Could not allocate %d bytes for a packet descriptor message buffer!", + __func__, fc->snaplen); + return DAQ_ERROR_NOMEM; + } + pool->info.mem_size += fc->snaplen; + + /* Initialize non-zero invariant packet header fields. */ + DAQ_PktHdr_t *pkthdr = &desc->pkthdr; + pkthdr->address_space_id = 0; + pkthdr->ingress_index = DAQ_PKTHDR_UNKNOWN; + pkthdr->ingress_group = DAQ_PKTHDR_UNKNOWN; + pkthdr->egress_index = DAQ_PKTHDR_UNKNOWN; + pkthdr->egress_group = DAQ_PKTHDR_UNKNOWN; + pkthdr->flags = 0; + + /* Initialize non-zero invariant message header fields. */ + DAQ_Msg_t *msg = &desc->msg; + msg->owner = fc->modinst; + msg->priv = desc; + + /* Place it on the free list */ + desc->next = pool->freelist; + pool->freelist = desc; + + pool->info.size++; + } + pool->info.available = pool->info.size; + return DAQ_SUCCESS; +} //------------------------------------------------------------------------- // file functions //------------------------------------------------------------------------- -static int file_setup(FileImpl* impl) +static int file_setup(FileContext* fc) { - if ( !strcmp(impl->name, "tty") ) + if ( !strcmp(fc->filename, "tty") ) { - impl->fid = STDIN_FILENO; + fc->fid = STDIN_FILENO; } - else if ( (impl->fid = open(impl->name, O_RDONLY|O_NONBLOCK)) < 0 ) + else if ( (fc->fid = open(fc->filename, O_RDONLY|O_NONBLOCK)) < 0 ) { char error_msg[1024] = {0}; if (strerror_r(errno, error_msg, sizeof(error_msg)) == 0) - DPE(impl->error, "%s: can't open file (%s)\n", DAQ_NAME, error_msg); + SET_ERROR(fc->modinst, "%s: can't open file (%s)", DAQ_NAME, error_msg); else - DPE(impl->error, "%s: can't open file: %d\n", DAQ_NAME, errno); + SET_ERROR(fc->modinst, "%s: can't open file: %d", DAQ_NAME, errno); return -1; } - impl->start = 1; + + fc->sof = true; + fc->eof = false; return 0; } -static void file_cleanup(FileImpl* impl) +static void file_cleanup(FileContext* fc) { - if ( impl->fid > STDIN_FILENO ) - close(impl->fid); + if ( fc->fid > STDIN_FILENO ) + close(fc->fid); - impl->fid = -1; -} - -static int file_read(FileImpl* impl) -{ - int n = read(impl->fid, impl->buf, impl->snaplen); - - if ( !n ) - { - if ( !impl->eof ) - { - impl->eof = 1; - return 1; // <= zero won't make it :( - } - return DAQ_READFILE_EOF; - } - - if ( n < 0 ) - { - if (errno != EINTR) - { - char error_msg[1024] = {0}; - if (strerror_r(errno, error_msg, sizeof(error_msg)) == 0) - DPE(impl->error, "%s: can't read from file (%s)\n", - DAQ_NAME, error_msg); - else - DPE(impl->error, "%s: can't read from file: %d\n", - DAQ_NAME, errno); - } - return DAQ_ERROR; - } - return n; + fc->fid = -1; } //------------------------------------------------------------------------- // daq utilities //------------------------------------------------------------------------- -static void set_pkt_hdr(FileImpl* impl, DAQ_PktHdr_t* phdr, ssize_t len) +static void init_packet_message(FileContext* fc, FileMsgDesc* desc) { - struct timeval t; - gettimeofday(&t, NULL); + DAQ_PktHdr_t *pkthdr = &desc->pkthdr; - phdr->ts.tv_sec = t.tv_sec; - phdr->ts.tv_usec = t.tv_usec; - phdr->caplen = phdr->pktlen = len; + desc->msg.type = DAQ_MSG_TYPE_PACKET; + desc->msg.hdr_len = sizeof(*pkthdr); + desc->msg.hdr = pkthdr; + desc->msg.data_len = 0; + desc->msg.data = desc->data; - phdr->ingress_index = phdr->egress_index = -1; - phdr->ingress_group = phdr->egress_group = -1; + struct timeval t; + gettimeofday(&t, NULL); - phdr->flags = 0; - phdr->address_space_id = 0; - phdr->opaque = 0; + pkthdr->ts.tv_sec = t.tv_sec; + pkthdr->ts.tv_usec = t.tv_usec; - if ( impl->start ) + desc->pci = fc->pci; + if (fc->sof) { - impl->pci.flags = DAQ_USR_FLAG_START_FLOW; - impl->start = 0; + desc->pci.flags |= DAQ_USR_FLAG_START_FLOW; + fc->sof = false; } - else if ( impl->eof ) - impl->pci.flags = DAQ_USR_FLAG_END_FLOW; - - else - impl->pci.flags = 0; - - phdr->priv_ptr = &impl->pci; } -static int file_daq_process( - FileImpl* impl, DAQ_Analysis_Func_t cb, void* user) +static DAQ_RecvStatus file_read_message(FileContext* fc, FileMsgDesc* desc) { - DAQ_PktHdr_t hdr; - int n = file_read(impl); + desc->msg.data = NULL; + int n = read(fc->fid, desc->data, fc->snaplen); - if ( n < 1 ) - return n; - - set_pkt_hdr(impl, &hdr, n); - DAQ_Verdict verdict = cb(user, &hdr, impl->buf); + if ( n ) + { + init_packet_message(fc, desc); + desc->msg.data_len = n; + } + else + { + // create an empty packet message to convey the End of Flow + if (!fc->eof) + { + init_packet_message(fc, desc); + desc->pci.flags |= DAQ_USR_FLAG_END_FLOW; + fc->eof = true; + return DAQ_RSTAT_EOF; + } + } - if ( verdict >= MAX_DAQ_VERDICT ) - verdict = DAQ_VERDICT_BLOCK; + if ( n < 0 ) + { + if (errno != EINTR) + { + char error_msg[1024] = {0}; + if (strerror_r(errno, error_msg, sizeof(error_msg)) == 0) + SET_ERROR(fc->modinst, "%s: can't read from file (%s)", DAQ_NAME, error_msg); + else + SET_ERROR(fc->modinst, "%s: can't read from file: %d", DAQ_NAME, errno); + return DAQ_RSTAT_ERROR; + } + } - impl->stats.verdicts[verdict]++; - return n; + return DAQ_RSTAT_OK; } //------------------------------------------------------------------------- // daq //------------------------------------------------------------------------- -static void file_daq_shutdown (void* handle) +static int file_daq_module_load(const DAQ_BaseAPI_t* base_api) { - FileImpl* impl = (FileImpl*)handle; - - if ( impl->name ) - free(impl->name); + if (base_api->api_version != DAQ_BASE_API_VERSION || base_api->api_size != sizeof(DAQ_BaseAPI_t)) + return DAQ_ERROR; - if ( impl->buf ) - free(impl->buf); + daq_base_api = *base_api; - free(impl); + return DAQ_SUCCESS; } -//------------------------------------------------------------------------- - -static int file_daq_initialize ( - const DAQ_Config_t* cfg, void** handle, char* errBuf, size_t errMax) +static int file_daq_instantiate(const DAQ_ModuleConfig_h modcfg, DAQ_ModuleInstance_h modinst, void** ctxt_ptr) { - FileImpl* impl = calloc(1, sizeof(*impl)); + FileContext* fc; + int rval = DAQ_ERROR; - if ( !impl ) + fc = calloc(1, sizeof(*fc)); + if (!fc) { - snprintf(errBuf, errMax, "%s: failed to allocate the ipfw context", DAQ_NAME); - return DAQ_ERROR_NOMEM; + SET_ERROR(modinst, "%s: Couldn't allocate memory for the new File context!", DAQ_NAME); + rval = DAQ_ERROR_NOMEM; + goto err; } + fc->modinst = modinst; - impl->fid = -1; - impl->start = impl->stop = 0; - impl->snaplen = cfg->snaplen ? cfg->snaplen : FILE_BUF_SZ; + fc->snaplen = daq_base_api.config_get_snaplen(modcfg) ? daq_base_api.config_get_snaplen(modcfg) : FILE_BUF_SZ; + fc->fid = -1; - if ( cfg->name ) + const char* filename = daq_base_api.config_get_input(modcfg); + if (filename) { - if ( !(impl->name = strdup(cfg->name)) ) + if (!(fc->filename = strdup(filename))) { - snprintf(errBuf, errMax, "%s: failed to allocate the filename", DAQ_NAME); - free(impl); - return DAQ_ERROR_NOMEM; + SET_ERROR(modinst, "%s: Couldn't allocate memory for the filename!", DAQ_NAME); + rval = DAQ_ERROR_NOMEM; + goto err; } } - if ( !(impl->buf = malloc(impl->snaplen)) ) - { - snprintf(errBuf, errMax, "%s: failed to allocate the ipfw buffer", DAQ_NAME); - file_daq_shutdown(impl); - return DAQ_ERROR_NOMEM; - } + uint32_t pool_size = daq_base_api.config_get_msg_pool_size(modcfg); + rval = create_message_pool(fc, pool_size ? pool_size : FILE_DEFAULT_POOL_SIZE); + if (rval != DAQ_SUCCESS) + goto err; - impl->state = DAQ_STATE_INITIALIZED; + *ctxt_ptr = fc; - *handle = impl; return DAQ_SUCCESS; + +err: + if (fc) + { + if (fc->filename) + free(fc->filename); + destroy_message_pool(fc); + free(fc); + } + return rval; } -//------------------------------------------------------------------------- +static void file_daq_destroy(void* handle) +{ + FileContext* fc = (FileContext*) handle; -static int file_daq_start (void* handle) + if (fc->filename) + free(fc->filename); + destroy_message_pool(fc); + free(fc); +} + +static int file_daq_start(void* handle) { - FileImpl* impl = (FileImpl*)handle; + FileContext* fc = (FileContext*) handle; - if ( file_setup(impl) ) + if (file_setup(fc)) return DAQ_ERROR; - impl->state = DAQ_STATE_STARTED; return DAQ_SUCCESS; } -static int file_daq_stop (void* handle) +static int file_daq_interrupt(void* handle) { - FileImpl* impl = (FileImpl*)handle; - file_cleanup(impl); - impl->state = DAQ_STATE_STOPPED; + FileContext* fc = (FileContext*) handle; + fc->interrupted = true; return DAQ_SUCCESS; } -//------------------------------------------------------------------------- - -static int file_daq_inject ( - void* handle, const DAQ_PktHdr_t* hdr, const uint8_t* buf, uint32_t len, - int rev) +static int file_daq_stop (void* handle) { - (void)handle; - (void)hdr; - (void)buf; - (void)len; - (void)rev; - return DAQ_ERROR; + FileContext* fc = (FileContext*) handle; + file_cleanup(fc); + return DAQ_SUCCESS; } -//------------------------------------------------------------------------- - -static int file_daq_acquire ( - void* handle, int cnt, DAQ_Analysis_Func_t callback, DAQ_Meta_Func_t meta, void* user) +static int file_daq_ioctl(void* handle, DAQ_IoctlCmd cmd, void* arg, size_t arglen) { - (void)meta; + (void) handle; - FileImpl* impl = (FileImpl*)handle; - int hit = 0, miss = 0; - impl->stop = 0; - - while ( (hit < cnt || cnt <= 0) && !impl->stop ) + if (cmd == DIOCTL_QUERY_USR_PCI) { - int status = file_daq_process(impl, callback, user); - - if ( status > 0 ) - { - hit++; - miss = 0; - } - else if ( status < 0 ) - return status; - - else if ( ++miss == 2 ) - break; + if (arglen != sizeof(DIOCTL_QueryUsrPCI)) + return DAQ_ERROR_INVAL; + DIOCTL_QueryUsrPCI* qup = (DIOCTL_QueryUsrPCI*) arg; + if (!qup->msg) + return DAQ_ERROR_INVAL; + FileMsgDesc* desc = (FileMsgDesc*) qup->msg->priv; + qup->pci = &desc->pci; + return DAQ_SUCCESS; } - return DAQ_SUCCESS; -} - -//------------------------------------------------------------------------- - -static int file_daq_breakloop (void* handle) -{ - FileImpl* impl = (FileImpl*)handle; - impl->stop = 1; - return DAQ_SUCCESS; -} - -static DAQ_State file_daq_check_status (void* handle) -{ - FileImpl* impl = (FileImpl*)handle; - return impl->state; + return DAQ_ERROR_NOTSUP; } -static int file_daq_get_stats (void* handle, DAQ_Stats_t* stats) +static int file_daq_get_stats(void* handle, DAQ_Stats_t* stats) { - FileImpl* impl = (FileImpl*)handle; - *stats = impl->stats; + FileContext* fc = (FileContext*) handle; + memcpy(stats, &fc->stats, sizeof(DAQ_Stats_t)); return DAQ_SUCCESS; } -static void file_daq_reset_stats (void* handle) +static void file_daq_reset_stats(void* handle) { - FileImpl* impl = (FileImpl*)handle; - memset(&impl->stats, 0, sizeof(impl->stats)); + FileContext* fc = (FileContext*) handle; + memset(&fc->stats, 0, sizeof(fc->stats)); } static int file_daq_get_snaplen (void* handle) { - FileImpl* impl = (FileImpl*)handle; - return impl->snaplen; + FileContext* fc = (FileContext*) handle; + return fc->snaplen; } -static uint32_t file_daq_get_capabilities (void* handle) +static uint32_t file_daq_get_capabilities(void* handle) { - (void)handle; + (void) handle; return DAQ_CAPA_BLOCK | DAQ_CAPA_REPLACE | DAQ_CAPA_INJECT | DAQ_CAPA_INJECT_RAW - | DAQ_CAPA_BREAKLOOP | DAQ_CAPA_UNPRIV_START; + | DAQ_CAPA_INTERRUPT | DAQ_CAPA_UNPRIV_START; } static int file_daq_get_datalink_type(void *handle) @@ -343,82 +395,111 @@ static int file_daq_get_datalink_type(void *handle) return DLT_USER; } -static const char* file_daq_get_errbuf (void* handle) +static unsigned file_daq_msg_receive(void* handle, const unsigned max_recv, const DAQ_Msg_t* msgs[], DAQ_RecvStatus* rstat) { - FileImpl* impl = (FileImpl*)handle; - return impl->error; -} + FileContext* fc = (FileContext*) handle; + DAQ_RecvStatus status = DAQ_RSTAT_OK; + unsigned idx = 0; -static void file_daq_set_errbuf (void* handle, const char* s) -{ - FileImpl* impl = (FileImpl*)handle; - DPE(impl->error, "%s", s ? s : ""); -} + while (idx < max_recv) + { + /* Check to see if the receive has been canceled. If so, reset it and return appropriately. */ + if (fc->interrupted) + { + fc->interrupted = false; + status = DAQ_RSTAT_INTERRUPTED; + break; + } -static int file_daq_get_device_index(void* handle, const char* device) -{ - (void)handle; - (void)device; - return DAQ_ERROR_NOTSUP; + /* Make sure that we have a message descriptor available to populate. */ + FileMsgDesc* desc = fc->pool.freelist; + if (!desc) + { + status = DAQ_RSTAT_NOBUF; + break; + } + + /* Attempt to read a message into the descriptor. */ + status = file_read_message(fc, desc); + if (status != DAQ_RSTAT_OK) + break; + + /* Last, but not least, extract this descriptor from the free list and + place the message in the return vector. */ + fc->pool.freelist = desc->next; + desc->next = NULL; + fc->pool.info.available--; + msgs[idx] = &desc->msg; + + idx++; + } + + *rstat = status; + + return idx; } -static int file_daq_set_filter (void* handle, const char* filter) +static int file_daq_msg_finalize(void* handle, const DAQ_Msg_t* msg, DAQ_Verdict verdict) { - (void)handle; - (void)filter; - return DAQ_ERROR_NOTSUP; + FileContext* fc = (FileContext*) handle; + FileMsgDesc* desc = (FileMsgDesc *) msg->priv; + + if (verdict >= MAX_DAQ_VERDICT) + verdict = DAQ_VERDICT_PASS; + fc->stats.verdicts[verdict]++; + + /* Toss the descriptor back on the free list for reuse. */ + desc->next = fc->pool.freelist; + fc->pool.freelist = desc; + fc->pool.info.available++; + + return DAQ_SUCCESS; } -static int file_query_flow(void* handle, const DAQ_PktHdr_t* hdr, DAQ_QueryFlow_t* query) +static int file_daq_get_msg_pool_info(void* handle, DAQ_MsgPoolInfo_t* info) { - FileImpl* impl = (FileImpl*)handle; + FileContext* fc = (FileContext*) handle; - if ( hdr->priv_ptr != &impl->pci ) // sanity check - return DAQ_ERROR_INVAL; + *info = fc->pool.info; - if ( query->type == DAQ_USR_QUERY_PCI ) - { - query->value = &impl->pci; - query->length = sizeof(impl->pci); - return DAQ_SUCCESS; - } - return DAQ_ERROR_NOTSUP; + return DAQ_SUCCESS; } //------------------------------------------------------------------------- #ifdef BUILDING_SO -DAQ_SO_PUBLIC DAQ_Module_t DAQ_MODULE_DATA = +DAQ_SO_PUBLIC const DAQ_ModuleAPI_t DAQ_MODULE_DATA = #else -DAQ_Module_t file_daq_module_data = +const DAQ_ModuleAPI_t file_daq_module_data = #endif { - .api_version = DAQ_API_VERSION, - .module_version = DAQ_MOD_VERSION, - .name = DAQ_NAME, - .type = DAQ_TYPE, - .initialize = file_daq_initialize, - .set_filter = file_daq_set_filter, - .start = file_daq_start, - .acquire = file_daq_acquire, - .inject = file_daq_inject, - .breakloop = file_daq_breakloop, - .stop = file_daq_stop, - .shutdown = file_daq_shutdown, - .check_status = file_daq_check_status, - .get_stats = file_daq_get_stats, - .reset_stats = file_daq_reset_stats, - .get_snaplen = file_daq_get_snaplen, - .get_capabilities = file_daq_get_capabilities, - .get_datalink_type = file_daq_get_datalink_type, - .get_errbuf = file_daq_get_errbuf, - .set_errbuf = file_daq_set_errbuf, - .get_device_index = file_daq_get_device_index, - .modify_flow = NULL, - .hup_prep = NULL, - .hup_apply = NULL, - .hup_post = NULL, - .dp_add_dc = NULL, - .query_flow = file_query_flow + /* .api_version = */ DAQ_MODULE_API_VERSION, + /* .api_size = */ sizeof(DAQ_ModuleAPI_t), + /* .module_version = */ DAQ_MOD_VERSION, + /* .name = */ DAQ_NAME, + /* .type = */ DAQ_TYPE, + /* .load = */ file_daq_module_load, + /* .unload = */ NULL, + /* .get_variable_descs = */ NULL, + /* .instantiate = */ file_daq_instantiate, + /* .destroy = */ file_daq_destroy, + /* .set_filter = */ NULL, + /* .start = */ file_daq_start, + /* .inject = */ NULL, + /* .inject_relative = */ NULL, + /* .interrupt = */ file_daq_interrupt, + /* .stop = */ file_daq_stop, + /* .ioctl = */ file_daq_ioctl, + /* .get_stats = */ file_daq_get_stats, + /* .reset_stats = */ file_daq_reset_stats, + /* .get_snaplen = */ file_daq_get_snaplen, + /* .get_capabilities = */ file_daq_get_capabilities, + /* .get_datalink_type = */ file_daq_get_datalink_type, + /* .config_load = */ NULL, + /* .config_swap = */ NULL, + /* .config_free = */ NULL, + /* .msg_receive = */ file_daq_msg_receive, + /* .msg_finalize = */ file_daq_msg_finalize, + /* .get_msg_pool_info = */ file_daq_get_msg_pool_info, }; diff --git a/daqs/daq_hext.c b/daqs/daq_hext.c index a735089d2..e3aac549c 100644 --- a/daqs/daq_hext.c +++ b/daqs/daq_hext.c @@ -36,60 +36,143 @@ #include #include -#include -#include +#include -#define DAQ_MOD_VERSION 0 +#define DAQ_MOD_VERSION 1 #define DAQ_NAME "hext" #define DAQ_TYPE (DAQ_TYPE_FILE_CAPABLE|DAQ_TYPE_INTF_CAPABLE|DAQ_TYPE_MULTI_INSTANCE) +#define HEXT_DEFAULT_POOL_SIZE 16 #define DEF_BUF_SZ 16384 #define MAX_LINE_SZ 128 -typedef struct { - char* name; - FILE* fyle; +#define SET_ERROR(modinst, ...) daq_base_api.set_errbuf(modinst, __VA_ARGS__) - bool start; - bool stop; - bool eof; - int dlt; +typedef struct _hext_msg_desc +{ + DAQ_Msg_t msg; + DAQ_PktHdr_t pkthdr; + Flow_Stats_t flowstats; + DAQ_UsrHdr_t pci; + uint8_t* data; + struct _hext_msg_desc* next; +} HextMsgDesc; +typedef struct +{ + HextMsgDesc* pool; + HextMsgDesc* freelist; + DAQ_MsgPoolInfo_t info; +} HextMsgPool; + +typedef struct +{ + /* Configuration */ + char* filename; unsigned snaplen; - unsigned idx; + int dlt; - uint8_t* buf; - char line[MAX_LINE_SZ]; - char error[DAQ_ERRBUF_SIZE]; + /* State */ + DAQ_ModuleInstance_h modinst; + HextMsgPool pool; + FILE* fp; + volatile bool interrupted; + + bool sof; + bool eof; DAQ_UsrHdr_t pci; DAQ_UsrHdr_t cfg; - DAQ_State state; DAQ_Stats_t stats; +} HextContext; + +static DAQ_VariableDesc_t hext_variable_descriptions[] = { + { "dlt", "Data link type to report to the application instead of DLT_USER (integer)", DAQ_VAR_DESC_REQUIRES_ARGUMENT }, +}; - bool meta; - DAQ_MetaHdr_t hdr; - Flow_Stats_t flow; -} HextImpl; +static DAQ_BaseAPI_t daq_base_api; //------------------------------------------------------------------------- // utility functions //------------------------------------------------------------------------- -static void set_c2s(HextImpl* impl, int c2s) +static void destroy_message_pool(HextContext* hc) { - if ( c2s ) + HextMsgPool* pool = &hc->pool; + if (pool->pool) { - impl->pci = impl->cfg; + while (pool->info.size > 0) + free(pool->pool[--pool->info.size].data); + free(pool->pool); + pool->pool = NULL; + } + pool->freelist = NULL; + pool->info.available = 0; + pool->info.mem_size = 0; +} + +static int create_message_pool(HextContext* hc, unsigned size) +{ + HextMsgPool* pool = &hc->pool; + pool->pool = calloc(sizeof(HextMsgDesc), size); + if (!pool->pool) + { + SET_ERROR(hc->modinst, "%s: Could not allocate %zu bytes for a packet descriptor pool!", + __func__, sizeof(HextMsgDesc) * size); + return DAQ_ERROR_NOMEM; + } + pool->info.mem_size = sizeof(HextMsgDesc) * size; + while (pool->info.size < size) + { + /* Allocate packet data and set up descriptor */ + HextMsgDesc *desc = &pool->pool[pool->info.size]; + desc->data = malloc(hc->snaplen); + if (!desc->data) + { + SET_ERROR(hc->modinst, "%s: Could not allocate %d bytes for a packet descriptor message buffer!", + __func__, hc->snaplen); + return DAQ_ERROR_NOMEM; + } + pool->info.mem_size += hc->snaplen; + + /* Initialize non-zero invariant packet header fields. */ + DAQ_PktHdr_t *pkthdr = &desc->pkthdr; + pkthdr->address_space_id = 0; + pkthdr->ingress_index = DAQ_PKTHDR_UNKNOWN; + pkthdr->ingress_group = DAQ_PKTHDR_UNKNOWN; + pkthdr->egress_index = DAQ_PKTHDR_UNKNOWN; + pkthdr->egress_group = DAQ_PKTHDR_UNKNOWN; + pkthdr->flags = 0; + + /* Initialize non-zero invariant message header fields. */ + DAQ_Msg_t *msg = &desc->msg; + msg->owner = hc->modinst; + msg->priv = desc; + + /* Place it on the free list */ + desc->next = pool->freelist; + pool->freelist = desc; + + pool->info.size++; + } + pool->info.available = pool->info.size; + return DAQ_SUCCESS; +} + +static void set_c2s(HextContext* hc, int c2s) +{ + if (c2s) + { + hc->pci = hc->cfg; } else { - impl->pci.src_addr = impl->cfg.dst_addr; - impl->pci.dst_addr = impl->cfg.src_addr; - impl->pci.src_port = impl->cfg.dst_port; - impl->pci.dst_port = impl->cfg.src_port; - impl->pci.flags &= ~DAQ_USR_FLAG_TO_SERVER; + hc->pci.src_addr = hc->cfg.dst_addr; + hc->pci.dst_addr = hc->cfg.src_addr; + hc->pci.src_port = hc->cfg.dst_port; + hc->pci.dst_port = hc->cfg.src_port; + hc->pci.flags &= ~DAQ_USR_FLAG_TO_SERVER; } } @@ -98,13 +181,13 @@ static void parse_host(const char* s, uint32_t* addr, uint16_t* port) char buf[32]; // oversize so pton() errors out if too long unsigned c = 0; - while ( isspace(*s) ) + while (isspace(*s)) s++; - while ( *s && !isspace(*s) && c < sizeof(buf) ) + while (*s && !isspace(*s) && c < sizeof(buf)) buf[c++] = *s++; - if ( c == sizeof(buf) ) + if (c == sizeof(buf)) --c; buf[c] = '\0'; @@ -113,30 +196,30 @@ static void parse_host(const char* s, uint32_t* addr, uint16_t* port) *port = atoi(s); } -static void parse_pci(HextImpl* impl, const char* s) +static void parse_pci(HextContext* hc, const char* s) { - parse_host(s, &impl->pci.src_addr, &impl->pci.src_port); + parse_host(s, &hc->pci.src_addr, &hc->pci.src_port); s = strstr(s, "->"); - if ( !s ) + if (!s) return; - parse_host(s+2, &impl->pci.dst_addr, &impl->pci.dst_port); + parse_host(s+2, &hc->pci.dst_addr, &hc->pci.dst_port); // hack until client / server is resolved: - if ( impl->pci.src_port >= impl->pci.dst_port ) - impl->pci.flags |= DAQ_USR_FLAG_TO_SERVER; + if (hc->pci.src_port >= hc->pci.dst_port) + hc->pci.flags |= DAQ_USR_FLAG_TO_SERVER; else - impl->pci.flags &= ~DAQ_USR_FLAG_TO_SERVER; + hc->pci.flags &= ~DAQ_USR_FLAG_TO_SERVER; } static bool is_ipv4(char const* src) { struct in6_addr temp; - if ( inet_pton(AF_INET, src, &temp) == 1 ) + if (inet_pton(AF_INET, src, &temp) == 1) return true; - else if ( inet_pton(AF_INET6, src, &temp) == 1 ) + else if (inet_pton(AF_INET6, src, &temp) == 1) return false; return false; @@ -144,200 +227,53 @@ static bool is_ipv4(char const* src) static void IpAddr(uint32_t* addr, char const* ip) { - if ( is_ipv4(ip) ) { + if (is_ipv4(ip)) + { addr[0] = 0; addr[1] = 0; addr[2] = htonl(0xffff); inet_pton(AF_INET, ip, &addr[3]); } - else { + else inet_pton(AF_INET6, ip, addr); - } } -enum Search { - I_ZONE, - E_ZONE, - I_INT, - E_INT, - SRC_HOST, - SRC_PORT, - DST_HOST, - DST_PORT, - OPAQUE, - I_PKTS, - R_PKTS, - I_DROPPED, - R_DROPPED, - I_BYTES, - R_BYTES, - IS_QOS, - SOF_TIME, - EOF_TIME, - VLAN_TAG, - ADDR_SPACE, - PROTO, - END -}; - -static void set_flowstats(Flow_Stats_t* f, enum Search state, const char* s) +static bool parse_flowstats(DAQ_MsgType type, const char* line, HextMsgDesc *desc) { - switch (state) - { - case I_ZONE: - f->ingressZone = atoi(s); - break; - - case E_ZONE: - f->egressZone = atoi(s); - break; - - case I_INT: - f->ingressIntf = atoi(s); - break; - - case E_INT: - f->egressIntf = atoi(s); - break; - - case SRC_HOST: - IpAddr((uint32_t*)&f->initiatorIp, s); - break; - - case SRC_PORT: - f->initiatorPort = htons(atoi(s)); - break; - - case DST_HOST: - IpAddr((uint32_t*)&f->responderIp, s); - break; - - case DST_PORT: - f->responderPort = htons(atoi(s)); - break; - - case OPAQUE: - f->opaque = atoi(s); - break; - - case I_PKTS: - f->initiatorPkts = atoi(s); - break; - - case R_PKTS: - f->responderPkts = atoi(s); - break; - - case I_DROPPED: - f->initiatorPktsDropped = atoi(s); - break; - - case R_DROPPED: - f->responderPktsDropped = atoi(s); - break; - - case I_BYTES: - f->initiatorBytesDropped = atoi(s); - break; - - case R_BYTES: - f->responderBytesDropped = atoi(s); - break; - - case IS_QOS: - f->isQoSAppliedOnSrcIntf = atoi(s); - break; - - case SOF_TIME: - f->sof_timestamp.tv_sec = atoi(s); - f->sof_timestamp.tv_usec = 0; - break; - - case EOF_TIME: - f->eof_timestamp.tv_sec = atoi(s); - f->sof_timestamp.tv_usec = 0; - break; - - case VLAN_TAG: - f->vlan_tag = atoi(s); - if (f->vlan_tag == 0) - f->vlan_tag = 0xfff; - break; - - case ADDR_SPACE: - f->address_space_id = atoi(s); - break; - - case PROTO: - f->protocol = atoi(s); - break; - - default: - break; - } -} - -static void parse_flowstats(HextImpl* impl, bool sof, const char* line) -{ - DAQ_MetaHdr_t* h = &impl->hdr; - Flow_Stats_t* f = &impl->flow; - - char token[INET6_ADDRSTRLEN]; - memset(token, 0, sizeof(token)); - - char* t = token; - const char* p = line; - - if ( sof ) - h->type = DAQ_METAHDR_TYPE_SOF; - else - h->type = DAQ_METAHDR_TYPE_EOF; - - enum Search search; - for (search = I_ZONE; search != END; p++) - { - if (p[0] == '\0') - return; - - if ((size_t)(t - token) >= sizeof(token) - 1) - return; - - if (isspace(p[0])) - { - if (t != token) - { - set_flowstats(f, search, token); - memset(token, 0, sizeof(token)); - t = token; - search++; - } - - continue; - } - else - { - *t = *p; - t++; - } - } +#define FLOWSTATS_FORMAT "%d %d %d %d %s %hu %s %hu %u %lu %lu %lu %lu %lu %lu %hhu %ld %ld %hu %hu %hhu" +#define FLOWSTATS_ITEMS 21 + Flow_Stats_t* f = &desc->flowstats; + char srcaddr[INET6_ADDRSTRLEN], dstaddr[INET6_ADDRSTRLEN]; + int rval = sscanf(line, FLOWSTATS_FORMAT, &f->ingressZone, &f->egressZone, &f->ingressIntf, + &f->egressIntf, srcaddr, &f->initiatorPort, dstaddr, &f->responderPort, &f->opaque, + &f->initiatorPkts, &f->responderPkts, &f->initiatorPktsDropped, &f->responderPktsDropped, + &f->initiatorBytesDropped, &f->responderBytesDropped, &f->isQoSAppliedOnSrcIntf, + &f->sof_timestamp.tv_sec, &f->eof_timestamp.tv_sec, &f->vlan_tag, &f->address_space_id, + &f->protocol); + if (rval != FLOWSTATS_ITEMS) + return false; - if (t != token) - set_flowstats(f, search-1, token); + desc->msg.type = type; + desc->msg.hdr_len = sizeof(desc->flowstats); + desc->msg.hdr = &desc->flowstats; + desc->msg.data_len = 0; + desc->msg.data = NULL; - impl->meta = true; - impl->line[0] = '\0'; -} + IpAddr((uint32_t*)&f->initiatorIp, srcaddr); + f->initiatorPort = htons(f->initiatorPort); + IpAddr((uint32_t*)&f->responderIp, dstaddr); + f->responderPort = htons(f->responderPort); + f->sof_timestamp.tv_usec = 0; + f->eof_timestamp.tv_usec = 0; + if (f->vlan_tag == 0) + f->vlan_tag = 0xfff; -static unsigned flush(HextImpl* impl) -{ - unsigned n = impl->idx; - impl->idx = 0; - return n; + return true; } static uint8_t xlat(char c) { - switch ( c ) + switch (c) { case 'r': return '\r'; case 'n': return '\n'; @@ -350,12 +286,12 @@ static uint8_t xlat(char c) static int unescape(char c, char* u) { static int esc = 0; - if ( !esc && c == '\\' ) + if (!esc && c == '\\') { esc = 1; return 0; } - else if ( esc ) + else if (esc) { esc = 0; *u = xlat(c); @@ -375,505 +311,496 @@ static int unescape(char c, char* u) // $packet -> server // $client // $server -static void parse_command(HextImpl* impl, char* s) +static bool parse_command(HextContext* hc, const char* s, HextMsgDesc *desc) { - if ( !strncmp(s, "packet -> client", 16) ) - set_c2s(impl, 0); + bool msg = false; - else if ( !strncmp(s, "packet -> server", 16) ) - set_c2s(impl, 1); + if (!strncmp(s, "packet -> client", 16)) + set_c2s(hc, 0); - else if ( !strncmp(s, "packet ", 7) ) - parse_pci(impl, s+7); + else if (!strncmp(s, "packet -> server", 16)) + set_c2s(hc, 1); - else if ( !strncmp(s, "client ", 7) ) - parse_host(s+7, &impl->cfg.src_addr, &impl->cfg.src_port); + else if (!strncmp(s, "packet ", 7)) + parse_pci(hc, s+7); - else if ( !strncmp(s, "server ", 7) ) - parse_host(s+7, &impl->cfg.dst_addr, &impl->cfg.dst_port); + else if (!strncmp(s, "client ", 7)) + parse_host(s+7, &hc->cfg.src_addr, &hc->cfg.src_port); - else if ( !strncmp(s, "sof ", 4) ) - parse_flowstats(impl, true, s+4); + else if (!strncmp(s, "server ", 7)) + parse_host(s+7, &hc->cfg.dst_addr, &hc->cfg.dst_port); - else if ( !strncmp(s, "eof ", 4) ) - parse_flowstats(impl, false, s+4); + else if (!strncmp(s, "sof ", 4)) + msg = parse_flowstats(DAQ_MSG_TYPE_SOF, s+4, desc); + + else if (!strncmp(s, "eof ", 4)) + msg = parse_flowstats(DAQ_MSG_TYPE_EOF, s+4, desc); + + return msg; } // load quoted string data into buffer up to snaplen -static void parse_string(HextImpl* impl, char* s) +static void parse_string(HextContext* hc, char* s, HextMsgDesc *desc) { char t; - while ( *s && *s != '"' && impl->idx < impl->snaplen ) + while (*s && *s != '"' && desc->msg.data_len < hc->snaplen) { - if ( unescape(*s++, &t) ) - impl->buf[impl->idx++] = t; + if (unescape(*s++, &t)) + desc->data[desc->msg.data_len++] = t; } + desc->pkthdr.pktlen = desc->msg.data_len; } // load hex data into buffer up to snaplen -static void parse_hex(HextImpl* impl, char* s) +static void parse_hex(HextContext* hc, char* s, HextMsgDesc *desc) { char* t = s; long x = strtol(t, &s, 16); - while ( *s && s != t && impl->idx < impl->snaplen ) + while (*s && s != t && desc->msg.data_len < hc->snaplen) { - impl->buf[impl->idx++] = (uint8_t)x; + desc->data[desc->msg.data_len++] = (uint8_t) x; x = strtol(t=s, &s, 16); } + desc->pkthdr.pktlen = desc->msg.data_len; +} + +static void init_packet_message(HextContext* hc, HextMsgDesc* desc) +{ + DAQ_PktHdr_t *pkthdr = &desc->pkthdr; + + desc->msg.type = DAQ_MSG_TYPE_PACKET; + desc->msg.hdr_len = sizeof(*pkthdr); + desc->msg.hdr = pkthdr; + desc->msg.data_len = 0; + desc->msg.data = desc->data; + + struct timeval t; + gettimeofday(&t, NULL); + + pkthdr->ts.tv_sec = t.tv_sec; + pkthdr->ts.tv_usec = t.tv_usec; + + desc->pci = hc->pci; + if (hc->sof) + { + desc->pci.flags |= DAQ_USR_FLAG_START_FLOW; + hc->sof = false; + } } -static int parse(HextImpl* impl) +static bool parse(HextContext* hc, char* line, HextMsgDesc* desc) { - char* s = impl->line; + char* s = line; + bool got_msg = false; - while ( isspace(*s) ) + while (isspace(*s)) s++; - switch ( *s ) + // Force a flush of the current packet message if we hit a blank line or command + switch (*s) { case '\0': - impl->line[0] = '\0'; - return flush(impl); + if (desc->msg.data) + got_msg = true; + break; case '#': break; case '$': - if ( impl->idx ) - return flush(impl); + // Do not reset the line buffer so that we can parse the command the next time through + if (desc->msg.data) + return true; - parse_command(impl, s+1); + got_msg = parse_command(hc, s+1, desc); break; case '"': - parse_string(impl, s+1); + if (!desc->msg.data) + init_packet_message(hc, desc); + parse_string(hc, s+1, desc); break; case 'x': - parse_hex(impl, s+1); + if (!desc->msg.data) + init_packet_message(hc, desc); + parse_hex(hc, s+1, desc); break; } - impl->line[0] = '\0'; - return 0; + line[0] = '\0'; + return got_msg; } -//------------------------------------------------------------------------- -// file functions -//------------------------------------------------------------------------- - -static int hext_setup(HextImpl* impl) +static DAQ_RecvStatus hext_read_message(HextContext* hc, HextMsgDesc* desc) { - if ( !strcmp(impl->name, "tty") ) - { - impl->fyle = stdin; - } - else if ( !(impl->fyle = fopen(impl->name, "r")) ) - { - char error_msg[1024] = {0}; - if (strerror_r(errno, error_msg, sizeof(error_msg)) == 0) - DPE(impl->error, "%s: can't open file (%s)\n", - DAQ_NAME, error_msg); - else - DPE(impl->error, "%s: can't open file: %d\n", - DAQ_NAME, errno); - return -1; - } - parse_host("192.168.1.2 12345", &impl->cfg.src_addr, &impl->cfg.src_port); - parse_host("10.1.2.3 80", &impl->cfg.dst_addr, &impl->cfg.dst_port); - - impl->cfg.ip_proto = impl->pci.ip_proto = IPPROTO_TCP; - impl->cfg.flags = impl->pci.flags = DAQ_USR_FLAG_TO_SERVER; - impl->start = true; - - return 0; -} - -static void hext_cleanup(HextImpl* impl) -{ - if ( impl->fyle != stdin ) - fclose(impl->fyle); - - impl->fyle = NULL; -} - -static int hext_read(HextImpl* impl) -{ - int n = 0; + char line[MAX_LINE_SZ]; + char* s = NULL; - while ( impl->line[0] || fgets(impl->line, sizeof(impl->line), impl->fyle) ) + desc->msg.data = NULL; + line[0] = '\0'; + while (line[0] != '\0' || (s = fgets(line, sizeof(line), hc->fp)) != NULL) { - if ( (n = parse(impl)) ) + // FIXIT-L Currently no error checking, just ignores bad lines + if (parse(hc, line, desc)) break; - - if (impl->meta) - return 0; } - if ( !n ) - n = flush(impl); - - if ( !n ) + if (!s) { - if ( (impl->dlt == DLT_USER) && !impl->eof ) + if (feof(hc->fp)) { - impl->eof = true; - return 1; // <= zero won't make it :( + // If there is still pending data in a packet message, mark it as End of Flow and flush it + // Otherwise, create an empty packet message to convey the End of Flow + // FIXIT-M - Make this actually the case, for now Snort can't handle data on packets marked EoF + /* + if (!hc->eof) + { + if (!desc->msg.data) + init_packet_message(hc, desc); + desc->pci.flags |= DAQ_USR_FLAG_END_FLOW; + hc->eof = true; + return DAQ_RSTAT_OK; + } + */ + if (desc->msg.data) + return DAQ_RSTAT_OK; + if (!hc->eof) + { + init_packet_message(hc, desc); + desc->pci.flags |= DAQ_USR_FLAG_END_FLOW; + hc->eof = true; + return DAQ_RSTAT_OK; + } + return DAQ_RSTAT_EOF; } - return DAQ_READFILE_EOF; - } - if ( n < 0 ) - { - if (errno != EINTR) + if (ferror(hc->fp)) { char error_msg[1024] = {0}; if (strerror_r(errno, error_msg, sizeof(error_msg)) == 0) - DPE(impl->error, "%s: can't read from file (%s)\n", - DAQ_NAME, error_msg); + SET_ERROR(hc->modinst, "%s: can't read from file (%s)\n", DAQ_NAME, error_msg); else - DPE(impl->error, "%s: can't read from file: %d\n", - DAQ_NAME, errno); + SET_ERROR(hc->modinst, "%s: can't read from file: %d\n", DAQ_NAME, errno); + return DAQ_RSTAT_ERROR; } - return DAQ_ERROR; } - return n; + + return DAQ_RSTAT_OK; } //------------------------------------------------------------------------- -// daq utilities +// file functions //------------------------------------------------------------------------- -static int get_vars ( - HextImpl* impl, const DAQ_Config_t* cfg, char* errBuf, size_t errMax -) { - const char* s = NULL; - DAQ_Dict* entry; - - for ( entry = cfg->values; entry; entry = entry->next) - { - if ( !strcmp(entry->key, "dlt") ) - s = entry->value; - - else - { - snprintf(errBuf, errMax, "unknown var (%s)", s); - return 0; - } - } - if ( s ) - impl->dlt = atoi(s); - - return 1; -} - -static void set_pkt_hdr(HextImpl* impl, DAQ_PktHdr_t* phdr, ssize_t len) +static int hext_setup(HextContext* hc) { - struct timeval t; - gettimeofday(&t, NULL); - - phdr->ts.tv_sec = t.tv_sec; - phdr->ts.tv_usec = t.tv_usec; - phdr->caplen = phdr->pktlen = len; - - phdr->ingress_index = phdr->egress_index = -1; - phdr->ingress_group = phdr->egress_group = -1; - - phdr->flags = 0; - phdr->address_space_id = 0; - phdr->opaque = 0; - - if ( impl->dlt != DLT_USER ) - { - phdr->priv_ptr = NULL; - return; - } - impl->pci.flags &= ~(DAQ_USR_FLAG_START_FLOW|DAQ_USR_FLAG_END_FLOW); - - if ( impl->start ) + if (!strcmp(hc->filename, "tty")) { - impl->pci.flags |= DAQ_USR_FLAG_START_FLOW; - impl->start = false; + hc->fp = stdin; } - else if ( impl->eof ) - impl->pci.flags |= DAQ_USR_FLAG_END_FLOW; - - phdr->priv_ptr = &impl->pci; -} - -static int hext_daq_process( - HextImpl* impl, DAQ_Analysis_Func_t cb, DAQ_Meta_Func_t mb, void* user) -{ - DAQ_PktHdr_t hdr; - int n = hext_read(impl); - - if (impl->meta && mb) + else if (!(hc->fp = fopen(hc->filename, "r"))) { - mb(user, (const DAQ_MetaHdr_t*)&impl->hdr, (const uint8_t*)&impl->flow); - impl->meta = false; - return 0; + char error_msg[1024] = {0}; + if (strerror_r(errno, error_msg, sizeof(error_msg)) == 0) + SET_ERROR(hc->modinst, "%s: can't open file (%s)\n", DAQ_NAME, error_msg); + else + SET_ERROR(hc->modinst, "%s: can't open file: %d\n", DAQ_NAME, errno); + return -1; } + parse_host("192.168.1.2 12345", &hc->cfg.src_addr, &hc->cfg.src_port); + parse_host("10.1.2.3 80", &hc->cfg.dst_addr, &hc->cfg.dst_port); - if ( n < 1 ) - return n; - - set_pkt_hdr(impl, &hdr, n); - DAQ_Verdict verdict = cb(user, &hdr, impl->buf); + hc->cfg.ip_proto = hc->pci.ip_proto = IPPROTO_TCP; + hc->cfg.flags = hc->pci.flags = DAQ_USR_FLAG_TO_SERVER; + hc->sof = true; + hc->eof = false; - if ( verdict >= MAX_DAQ_VERDICT ) - verdict = DAQ_VERDICT_BLOCK; - - impl->stats.verdicts[verdict]++; - return n; + return 0; } + //------------------------------------------------------------------------- // daq //------------------------------------------------------------------------- -static void hext_daq_shutdown (void* handle) +static int hext_daq_module_load(const DAQ_BaseAPI_t* base_api) { - HextImpl* impl = (HextImpl*)handle; - - if ( impl->name ) - free(impl->name); + if (base_api->api_version != DAQ_BASE_API_VERSION || base_api->api_size != sizeof(DAQ_BaseAPI_t)) + return DAQ_ERROR; - if ( impl->buf ) - free(impl->buf); + daq_base_api = *base_api; - free(impl); + return DAQ_SUCCESS; } -//------------------------------------------------------------------------- +static int hext_daq_get_variable_descs(const DAQ_VariableDesc_t** var_desc_table) +{ + *var_desc_table = hext_variable_descriptions; -static int hext_daq_initialize ( - const DAQ_Config_t* cfg, void** handle, char* errBuf, size_t errMax) + return sizeof(hext_variable_descriptions) / sizeof(DAQ_VariableDesc_t); +} + +static int hext_daq_instantiate(const DAQ_ModuleConfig_h modcfg, DAQ_ModuleInstance_h modinst, void** ctxt_ptr) { - HextImpl* impl = calloc(1, sizeof(*impl)); + HextContext* hc; + int rval = DAQ_ERROR; - if ( !impl ) + hc = calloc(1, sizeof(*hc)); + if (!hc) { - snprintf(errBuf, errMax, "%s: failed to allocate the ipfw context", DAQ_NAME); - return DAQ_ERROR_NOMEM; + SET_ERROR(modinst, "%s: Couldn't allocate memory for the new Hext context!", DAQ_NAME); + rval = DAQ_ERROR_NOMEM; + goto err; } + hc->modinst = modinst; - impl->snaplen = cfg->snaplen ? cfg->snaplen : DEF_BUF_SZ; - impl->dlt = DLT_USER; + hc->snaplen = daq_base_api.config_get_snaplen(modcfg) ? daq_base_api.config_get_snaplen(modcfg) : DEF_BUF_SZ; + hc->dlt = DLT_USER; - if ( !get_vars(impl, cfg, errBuf, errMax) ) + const char* varKey, * varValue; + daq_base_api.config_first_variable(modcfg, &varKey, &varValue); + while (varKey) { - free(impl); - return DAQ_ERROR; + /* Retrieve the requested buffer size (default = 0) */ + if (!strcmp(varKey, "dlt")) + hc->dlt = strtol(varValue, NULL, 10); + else + { + SET_ERROR(modinst, "%s: Unknown variable name: '%s'", DAQ_NAME, varKey); + rval = DAQ_ERROR_INVAL; + goto err; + } + + daq_base_api.config_next_variable(modcfg, &varKey, &varValue); } - if ( cfg->name ) + const char* filename = daq_base_api.config_get_input(modcfg); + if (filename) { - if ( !(impl->name = strdup(cfg->name)) ) + if (!(hc->filename = strdup(filename))) { - snprintf(errBuf, errMax, "%s: failed to allocate the filename", DAQ_NAME); - free(impl); - return DAQ_ERROR_NOMEM; + SET_ERROR(modinst, "%s: Couldn't allocate memory for the filename!", DAQ_NAME); + rval = DAQ_ERROR_NOMEM; + goto err; } } - if ( !(impl->buf = malloc(impl->snaplen)) ) - { - snprintf(errBuf, errMax, "%s: failed to allocate the ipfw buffer", DAQ_NAME); - hext_daq_shutdown(impl); - return DAQ_ERROR_NOMEM; - } + uint32_t pool_size = daq_base_api.config_get_msg_pool_size(modcfg); + rval = create_message_pool(hc, pool_size ? pool_size : HEXT_DEFAULT_POOL_SIZE); + if (rval != DAQ_SUCCESS) + goto err; - impl->state = DAQ_STATE_INITIALIZED; + *ctxt_ptr = hc; - *handle = impl; return DAQ_SUCCESS; + +err: + if (hc) + { + if (hc->filename) + free(hc->filename); + destroy_message_pool(hc); + free(hc); + } + return rval; } -//------------------------------------------------------------------------- +static void hext_daq_destroy(void* handle) +{ + HextContext* hc = (HextContext*) handle; -static int hext_daq_start (void* handle) + if (hc->filename) + free(hc->filename); + destroy_message_pool(hc); + free(hc); +} + +static int hext_daq_start(void* handle) { - HextImpl* impl = (HextImpl*)handle; + HextContext* hc = (HextContext*) handle; - if ( hext_setup(impl) ) + if (hext_setup(hc)) return DAQ_ERROR; - impl->state = DAQ_STATE_STARTED; return DAQ_SUCCESS; } -static int hext_daq_stop (void* handle) +static int hext_daq_interrupt(void* handle) { - HextImpl* impl = (HextImpl*)handle; - hext_cleanup(impl); - impl->state = DAQ_STATE_STOPPED; + HextContext* hc = (HextContext*) handle; + hc->interrupted = true; return DAQ_SUCCESS; } -//------------------------------------------------------------------------- - -static int hext_daq_inject ( - void* handle, const DAQ_PktHdr_t* hdr, const uint8_t* buf, uint32_t len, int rev) +static int hext_daq_stop(void* handle) { - (void)handle; - (void)hdr; - (void)buf; - (void)len; - (void)rev; - return DAQ_ERROR; -} - -//------------------------------------------------------------------------- - -static int hext_daq_acquire ( - void* handle, int cnt, DAQ_Analysis_Func_t callback, DAQ_Meta_Func_t meta, void* user) -{ - HextImpl* impl = (HextImpl*)handle; - int hit = 0, miss = 0; - impl->stop = false; + HextContext* hc = (HextContext*) handle; - while ( hit < cnt || cnt <= 0 ) - { - int status = hext_daq_process(impl, callback, meta, user); + if (hc->fp != stdin) + fclose(hc->fp); - if ( status > 0 ) - { - hit++; - miss = 0; - } - else if ( status < 0 ) - return status; + hc->fp = NULL; - else if ( ++miss == 2 || impl->stop ) - break; - } return DAQ_SUCCESS; } -//------------------------------------------------------------------------- - -static int hext_daq_breakloop (void* handle) +static int hext_daq_ioctl(void* handle, DAQ_IoctlCmd cmd, void* arg, size_t arglen) { - HextImpl* impl = (HextImpl*)handle; - impl->stop = true; - return DAQ_SUCCESS; -} + (void) handle; -static DAQ_State hext_daq_check_status (void* handle) -{ - HextImpl* impl = (HextImpl*)handle; - return impl->state; + if (cmd == DIOCTL_QUERY_USR_PCI) + { + if (arglen != sizeof(DIOCTL_QueryUsrPCI)) + return DAQ_ERROR_INVAL; + DIOCTL_QueryUsrPCI* qup = (DIOCTL_QueryUsrPCI*) arg; + if (!qup->msg) + return DAQ_ERROR_INVAL; + HextMsgDesc* desc = (HextMsgDesc*) qup->msg->priv; + qup->pci = &desc->pci; + return DAQ_SUCCESS; + } + return DAQ_ERROR_NOTSUP; } -static int hext_daq_get_stats (void* handle, DAQ_Stats_t* stats) +static int hext_daq_get_stats(void* handle, DAQ_Stats_t* stats) { - HextImpl* impl = (HextImpl*)handle; - *stats = impl->stats; + HextContext* hc = (HextContext*) handle; + memcpy(stats, &hc->stats, sizeof(DAQ_Stats_t)); return DAQ_SUCCESS; } -static void hext_daq_reset_stats (void* handle) +static void hext_daq_reset_stats(void* handle) { - HextImpl* impl = (HextImpl*)handle; - memset(&impl->stats, 0, sizeof(impl->stats)); + HextContext* hc = (HextContext*) handle; + memset(&hc->stats, 0, sizeof(hc->stats)); } static int hext_daq_get_snaplen (void* handle) { - HextImpl* impl = (HextImpl*)handle; - return impl->snaplen; + HextContext* hc = (HextContext*) handle; + return hc->snaplen; } -static uint32_t hext_daq_get_capabilities (void* handle) +static uint32_t hext_daq_get_capabilities(void* handle) { - (void)handle; + (void) handle; return DAQ_CAPA_BLOCK | DAQ_CAPA_REPLACE | DAQ_CAPA_INJECT | DAQ_CAPA_INJECT_RAW - | DAQ_CAPA_BREAKLOOP | DAQ_CAPA_UNPRIV_START; + | DAQ_CAPA_INTERRUPT | DAQ_CAPA_UNPRIV_START; } -static int hext_daq_get_datalink_type(void *handle) +static int hext_daq_get_datalink_type(void* handle) { - HextImpl* impl = (HextImpl*)handle; - return impl->dlt; + HextContext* hc = (HextContext*) handle; + return hc->dlt; } -static const char* hext_daq_get_errbuf (void* handle) +static unsigned hext_daq_msg_receive(void* handle, const unsigned max_recv, const DAQ_Msg_t* msgs[], DAQ_RecvStatus* rstat) { - HextImpl* impl = (HextImpl*)handle; - return impl->error; -} + HextContext* hc = (HextContext*) handle; + DAQ_RecvStatus status = DAQ_RSTAT_OK; + unsigned idx = 0; -static void hext_daq_set_errbuf (void* handle, const char* s) -{ - HextImpl* impl = (HextImpl*)handle; - DPE(impl->error, "%s", s ? s : ""); -} + while (idx < max_recv) + { + /* Check to see if the receive has been canceled. If so, reset it and return appropriately. */ + if (hc->interrupted) + { + hc->interrupted = false; + status = DAQ_RSTAT_INTERRUPTED; + break; + } -static int hext_daq_get_device_index(void* handle, const char* device) -{ - (void)handle; - (void)device; - return DAQ_ERROR_NOTSUP; + /* Make sure that we have a message descriptor available to populate. */ + HextMsgDesc* desc = hc->pool.freelist; + if (!desc) + { + status = DAQ_RSTAT_NOBUF; + break; + } + + /* Attempt to read a message into the descriptor. */ + status = hext_read_message(hc, desc); + if (status != DAQ_RSTAT_OK) + break; + + /* Last, but not least, extract this descriptor from the free list and + place the message in the return vector. */ + hc->pool.freelist = desc->next; + desc->next = NULL; + hc->pool.info.available--; + msgs[idx] = &desc->msg; + + idx++; + } + + *rstat = status; + + return idx; } -static int hext_daq_set_filter (void* handle, const char* filter) +static int hext_daq_msg_finalize(void* handle, const DAQ_Msg_t* msg, DAQ_Verdict verdict) { - (void)handle; - (void)filter; - return DAQ_ERROR_NOTSUP; + HextContext* hc = (HextContext*) handle; + HextMsgDesc* desc = (HextMsgDesc *) msg->priv; + + if (verdict >= MAX_DAQ_VERDICT) + verdict = DAQ_VERDICT_PASS; + hc->stats.verdicts[verdict]++; + + /* Toss the descriptor back on the free list for reuse. */ + desc->next = hc->pool.freelist; + hc->pool.freelist = desc; + hc->pool.info.available++; + + return DAQ_SUCCESS; } -static int hext_query_flow(void* handle, const DAQ_PktHdr_t* hdr, DAQ_QueryFlow_t* query) +static int hext_daq_get_msg_pool_info(void* handle, DAQ_MsgPoolInfo_t* info) { - HextImpl* impl = (HextImpl*)handle; + HextContext* hc = (HextContext*) handle; - if ( hdr->priv_ptr != &impl->pci ) // sanity check - return DAQ_ERROR_INVAL; + *info = hc->pool.info; - if ( query->type == DAQ_USR_QUERY_PCI ) - { - query->value = &impl->pci; - query->length = sizeof(impl->pci); - return DAQ_SUCCESS; - } - return DAQ_ERROR_NOTSUP; + return DAQ_SUCCESS; } //------------------------------------------------------------------------- #ifdef BUILDING_SO -DAQ_SO_PUBLIC DAQ_Module_t DAQ_MODULE_DATA = +DAQ_SO_PUBLIC const DAQ_ModuleAPI_t DAQ_MODULE_DATA = #else -DAQ_Module_t hext_daq_module_data = +const DAQ_ModuleAPI_t hext_daq_module_data = #endif { - .api_version = DAQ_API_VERSION, - .module_version = DAQ_MOD_VERSION, - .name = DAQ_NAME, - .type = DAQ_TYPE, - .initialize = hext_daq_initialize, - .set_filter = hext_daq_set_filter, - .start = hext_daq_start, - .acquire = hext_daq_acquire, - .inject = hext_daq_inject, - .breakloop = hext_daq_breakloop, - .stop = hext_daq_stop, - .shutdown = hext_daq_shutdown, - .check_status = hext_daq_check_status, - .get_stats = hext_daq_get_stats, - .reset_stats = hext_daq_reset_stats, - .get_snaplen = hext_daq_get_snaplen, - .get_capabilities = hext_daq_get_capabilities, - .get_datalink_type = hext_daq_get_datalink_type, - .get_errbuf = hext_daq_get_errbuf, - .set_errbuf = hext_daq_set_errbuf, - .get_device_index = hext_daq_get_device_index, - .modify_flow = NULL, - .hup_prep = NULL, - .hup_apply = NULL, - .hup_post = NULL, - .dp_add_dc = NULL, - .query_flow = hext_query_flow + /* .api_version = */ DAQ_MODULE_API_VERSION, + /* .api_size = */ sizeof(DAQ_ModuleAPI_t), + /* .module_version = */ DAQ_MOD_VERSION, + /* .name = */ DAQ_NAME, + /* .type = */ DAQ_TYPE, + /* .load = */ hext_daq_module_load, + /* .unload = */ NULL, + /* .get_variable_descs = */ hext_daq_get_variable_descs, + /* .instantiate = */ hext_daq_instantiate, + /* .destroy = */ hext_daq_destroy, + /* .set_filter = */ NULL, + /* .start = */ hext_daq_start, + /* .inject = */ NULL, + /* .inject_relative = */ NULL, + /* .interrupt = */ hext_daq_interrupt, + /* .stop = */ hext_daq_stop, + /* .ioctl = */ hext_daq_ioctl, + /* .get_stats = */ hext_daq_get_stats, + /* .reset_stats = */ hext_daq_reset_stats, + /* .get_snaplen = */ hext_daq_get_snaplen, + /* .get_capabilities = */ hext_daq_get_capabilities, + /* .get_datalink_type = */ hext_daq_get_datalink_type, + /* .config_load = */ NULL, + /* .config_swap = */ NULL, + /* .config_free = */ NULL, + /* .msg_receive = */ hext_daq_msg_receive, + /* .msg_finalize = */ hext_daq_msg_finalize, + /* .get_msg_pool_info = */ hext_daq_get_msg_pool_info, }; diff --git a/daqs/daq_user.h b/daqs/daq_user.h index 5f62c1325..afaada6ef 100644 --- a/daqs/daq_user.h +++ b/daqs/daq_user.h @@ -23,19 +23,16 @@ #define DAQ_USER_H #include +#include /* for raw payload only */ #define DLT_USER 230 -/* in: DAQ_QueryFlow_t.type */ -#define DAQ_USR_QUERY_PCI 1000 - /* DAQ_UsrHdr_t.flags */ #define DAQ_USR_FLAG_TO_SERVER 0x01 #define DAQ_USR_FLAG_START_FLOW 0x02 #define DAQ_USR_FLAG_END_FLOW 0x04 -/* out: DAQ_QueryFlow_t.value */ typedef struct { uint32_t src_addr; @@ -46,5 +43,12 @@ typedef struct uint8_t flags; } DAQ_UsrHdr_t; +#define DIOCTL_QUERY_USR_PCI (DAQ_IoctlCmd) 2048 +typedef struct +{ + DAQ_Msg_h msg; + DAQ_UsrHdr_t* pci; +} DIOCTL_QueryUsrPCI; + #endif diff --git a/lua/inline.lua b/lua/inline.lua index c9ea01edc..902334408 100644 --- a/lua/inline.lua +++ b/lua/inline.lua @@ -5,8 +5,17 @@ daq = { - module = 'dump', - variables = { "load-mode=read-file", "output=none" } + modules = + { + { + name = 'pcap', + mode = 'read-file' + }, + { + name = 'dump', + variables = { 'output = none' } + }, + }, } normalizer = { tcp = { ips = true } } diff --git a/lua/talos.lua b/lua/talos.lua index 42a57c5ed..c3262bc4b 100644 --- a/lua/talos.lua +++ b/lua/talos.lua @@ -5,9 +5,19 @@ daq = { - module = 'dump', - variables = { "load-mode=read-file", "output=none" } + modules = + { + { + name = 'pcap', + mode = 'read-file' + }, + { + name = 'dump', + variables = { 'output = none' } + }, + }, } + normalizer = { tcp = { ips = true } } ips.include = 'local.rules' diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 679bfdc22..327c512b2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,6 +48,10 @@ if ( USE_TIRPC ) LIST(APPEND EXTERNAL_INCLUDES ${TIRPC_INCLUDE_DIRS}) endif () +if ( ENABLE_STATIC_DAQ ) + LIST(APPEND EXTERNAL_LIBRARIES ${DAQ_STATIC_MODULE_LIBS}) +endif () + include_directories(BEFORE ${LUAJIT_INCLUDE_DIR}) include_directories(AFTER ${EXTERNAL_INCLUDES}) diff --git a/src/codecs/ip/cd_ipv4.cc b/src/codecs/ip/cd_ipv4.cc index 9fc599325..f47d11a0f 100644 --- a/src/codecs/ip/cd_ipv4.cc +++ b/src/codecs/ip/cd_ipv4.cc @@ -22,7 +22,7 @@ #include "config.h" #endif -#include +#include #include diff --git a/src/codecs/ip/cd_ipv6.cc b/src/codecs/ip/cd_ipv6.cc index 7dadfd86d..72b124000 100644 --- a/src/codecs/ip/cd_ipv6.cc +++ b/src/codecs/ip/cd_ipv6.cc @@ -22,7 +22,7 @@ #include "config.h" #endif -#include +#include #include "codecs/codec_module.h" #include "framework/codec.h" diff --git a/src/codecs/misc/cd_user.cc b/src/codecs/misc/cd_user.cc index 5a4d069f2..54e4a3c12 100644 --- a/src/codecs/misc/cd_user.cc +++ b/src/codecs/misc/cd_user.cc @@ -26,6 +26,7 @@ #include "daqs/daq_user.h" #include "framework/codec.h" #include "packet_io/sfdaq.h" +#include "packet_io/sfdaq_instance.h" using namespace snort; @@ -98,15 +99,12 @@ static void set_flags( bool UserCodec::decode(const RawData& raw, CodecData& codec, DecodeData& snort) { - DAQ_QueryFlow_t query { DAQ_USR_QUERY_PCI, 0, nullptr }; + DIOCTL_QueryUsrPCI qup = { raw.daq_msg, nullptr }; - if ( SFDAQ::get_local_instance()->query_flow(raw.pkth, &query) != DAQ_SUCCESS or - query.length != sizeof(DAQ_UsrHdr_t) ) - { + if ( SFDAQ::get_local_instance()->ioctl(DIOCTL_QUERY_USR_PCI, &qup, sizeof(qup)) != DAQ_SUCCESS ) return false; - } - const DAQ_UsrHdr_t* pci = (DAQ_UsrHdr_t*)query.value; + const DAQ_UsrHdr_t* pci = qup.pci; if ( !pci ) return false; diff --git a/src/codecs/root/cd_eth.cc b/src/codecs/root/cd_eth.cc index a602af2c7..531689b98 100644 --- a/src/codecs/root/cd_eth.cc +++ b/src/codecs/root/cd_eth.cc @@ -22,7 +22,7 @@ #include "config.h" #endif -#include +#include #include "codecs/codec_module.h" #include "framework/codec.h" diff --git a/src/codecs/root/cd_raw.cc b/src/codecs/root/cd_raw.cc index 776c089e7..c106ee90c 100644 --- a/src/codecs/root/cd_raw.cc +++ b/src/codecs/root/cd_raw.cc @@ -21,7 +21,7 @@ #include "config.h" #endif -#include +#include #include "framework/codec.h" diff --git a/src/detection/context_switcher.cc b/src/detection/context_switcher.cc index 5a0b91885..46b53c0ab 100644 --- a/src/detection/context_switcher.cc +++ b/src/detection/context_switcher.cc @@ -87,24 +87,21 @@ void ContextSwitcher::start() void ContextSwitcher::stop() { - assert(busy.size() == 1); - IpsContext* c = busy.back(); + assert(c); assert(c->state == IpsContext::BUSY); - assert(!c->has_callbacks()); assert(!c->dependencies()); trace_logf(detection, TRACE_DETECTION_ENGINE, "(wire) %" PRIu64 " cs::stop %" PRIu64 " (i=%zu, b=%zu)\n", get_packet_number(), c->context_num, idle.size(), busy.size()); - busy.pop_back(); - c->clear_context_data(); c->packet->active = nullptr; c->packet->action = nullptr; c->state = IpsContext::IDLE; + busy.pop_back(); idle.emplace_back(c); } diff --git a/src/detection/detection_engine.cc b/src/detection/detection_engine.cc index 1c0522497..67eee2d1f 100644 --- a/src/detection/detection_engine.cc +++ b/src/detection/detection_engine.cc @@ -30,8 +30,8 @@ #include "framework/endianness.h" #include "helpers/ring.h" #include "latency/packet_latency.h" +#include "main/analyzer.h" #include "main/modules.h" -#include "main/snort.h" #include "main/snort_config.h" #include "main/snort_debug.h" #include "main/thread.h" @@ -101,40 +101,44 @@ void DetectionEngine::thread_term() DetectionEngine::DetectionEngine() { - context = Snort::get_switcher()->interrupt(); + context = Analyzer::get_switcher()->interrupt(); context->file_data = { nullptr, 0 }; reset(); } DetectionEngine::~DetectionEngine() { - if ( context == Snort::get_switcher()->get_context() ) + if ( context == Analyzer::get_switcher()->get_context() ) { // finish_packet is called here so that we clear wire packets at the right time + // FIXIT-L if might not be needed anymore with wire packet checks in finish_packet finish_packet(context->packet, true); } } void DetectionEngine::reset() { - IpsContext* c = Snort::get_switcher()->get_context(); + IpsContext* c = Analyzer::get_switcher()->get_context(); c->alt_data.len = 0; // FIXIT-H need context::reset() } IpsContext* DetectionEngine::get_context() -{ return Snort::get_switcher()->get_context(); } +{ return Analyzer::get_switcher()->get_context(); } SF_EVENTQ* DetectionEngine::get_event_queue() -{ return Snort::get_switcher()->get_context()->equeue; } +{ return Analyzer::get_switcher()->get_context()->equeue; } Packet* DetectionEngine::get_current_packet() -{ return Snort::get_switcher()->get_context()->packet; } +{ return Analyzer::get_switcher()->get_context()->packet; } + +Packet* DetectionEngine::get_current_wire_packet() +{ return Analyzer::get_switcher()->get_context()->wire_packet; } void DetectionEngine::set_encode_packet(Packet* p) -{ Snort::get_switcher()->get_context()->encode_packet = p; } +{ Analyzer::get_switcher()->get_context()->encode_packet = p; } Packet* DetectionEngine::get_encode_packet() -{ return Snort::get_switcher()->get_context()->encode_packet; } +{ return Analyzer::get_switcher()->get_context()->encode_packet; } // we need to stay in the current context until rebuild is successful // any events while rebuilding will be logged against the current packet @@ -145,14 +149,18 @@ Packet* DetectionEngine::set_next_packet(Packet* parent) static THREAD_LOCAL IpsAction* shutdown_action = nullptr; wait_for_context(); - IpsContext* c = Snort::get_switcher()->get_next(); - if ( parent ) // FIXIT-L parent can probably be determined by busy queue + IpsContext* c = Analyzer::get_switcher()->get_next(); + if ( parent ) { c->snapshot_flow(parent->flow); c->packet_number = parent->context->packet_number; + c->wire_packet = parent->context->wire_packet; } else + { c->packet_number = get_packet_number(); + c->wire_packet = nullptr; + } Packet* p = c->packet; @@ -163,13 +171,17 @@ Packet* DetectionEngine::set_next_packet(Packet* parent) // normal rebuild if ( parent ) { + p->daq_msg = parent->daq_msg; + p->daq_instance = parent->daq_instance; p->active = parent->active; p->action = parent->action; } // processing but parent is already gone (flow cache flush etc..) - else if ( Snort::get_switcher()->get_context() ) + else if ( Analyzer::get_switcher()->get_context() ) { + p->daq_msg = nullptr; + p->daq_instance = nullptr; p->active = get_current_packet()->active; p->action = get_current_packet()->action; } @@ -177,6 +189,8 @@ Packet* DetectionEngine::set_next_packet(Packet* parent) // shutdown, so use a dummy so null checking is not needed everywhere else { + p->daq_msg = nullptr; + p->daq_instance = nullptr; p->action = &shutdown_action; p->active = &shutdown_active; shutdown_active.reset(); @@ -221,7 +235,7 @@ void DetectionEngine::finish_inspect(Packet* p, bool inspected) void DetectionEngine::finish_packet(Packet* p, bool flow_deletion) { - ContextSwitcher* sw = Snort::get_switcher(); + ContextSwitcher* sw = Analyzer::get_switcher(); log_events(p); clear_events(p); @@ -236,27 +250,18 @@ void DetectionEngine::finish_packet(Packet* p, bool flow_deletion) if ( flow_deletion or p->is_rebuilt() ) sw->complete(); - - // FIXIT-H enable for daqng -#if 0 - if ( !p->is_rebuilt() ) - { - sw->stop(); - queue for daq msg finalize - } -#endif } uint8_t* DetectionEngine::get_buffer(unsigned& max) { max = IpsContext::buf_size; - return Snort::get_switcher()->get_context()->buf; + return Analyzer::get_switcher()->get_context()->buf; } uint8_t* DetectionEngine::get_next_buffer(unsigned& max) { max = IpsContext::buf_size; - return Snort::get_switcher()->get_next()->buf; + return Analyzer::get_switcher()->get_next()->buf; } DataBuffer& DetectionEngine::get_alt_buffer(Packet* p) @@ -266,23 +271,23 @@ DataBuffer& DetectionEngine::get_alt_buffer(Packet* p) } void DetectionEngine::set_file_data(const DataPointer& dp) -{ Snort::get_switcher()->get_context()->file_data = dp; } +{ Analyzer::get_switcher()->get_context()->file_data = dp; } DataPointer& DetectionEngine::get_file_data(IpsContext* c) { return c->file_data; } void DetectionEngine::set_data(unsigned id, IpsContextData* p) -{ Snort::get_switcher()->get_context()->set_context_data(id, p); } +{ Analyzer::get_switcher()->get_context()->set_context_data(id, p); } IpsContextData* DetectionEngine::get_data(unsigned id) -{ return Snort::get_switcher()->get_context()->get_context_data(id); } +{ return Analyzer::get_switcher()->get_context()->get_context_data(id); } IpsContextData* DetectionEngine::get_data(unsigned id, IpsContext* context) { if ( context ) return context->get_context_data(id); - ContextSwitcher* sw = Snort::get_switcher(); + ContextSwitcher* sw = Analyzer::get_switcher(); if ( !sw ) return nullptr; @@ -296,26 +301,26 @@ void DetectionEngine::add_replacement(const std::string& s, unsigned off) r.data = s; r.offset = off; - Snort::get_switcher()->get_context()->rpl.emplace_back(r); + Analyzer::get_switcher()->get_context()->rpl.emplace_back(r); } bool DetectionEngine::get_replacement(std::string& s, unsigned& off) { - if ( Snort::get_switcher()->get_context()->rpl.empty() ) + if ( Analyzer::get_switcher()->get_context()->rpl.empty() ) return false; - auto rep = Snort::get_switcher()->get_context()->rpl.back(); + auto rep = Analyzer::get_switcher()->get_context()->rpl.back(); s = rep.data; off = rep.offset; - Snort::get_switcher()->get_context()->rpl.pop_back(); + Analyzer::get_switcher()->get_context()->rpl.pop_back(); return true; } void DetectionEngine::clear_replacement() { - Snort::get_switcher()->get_context()->rpl.clear(); + Analyzer::get_switcher()->get_context()->rpl.clear(); } void DetectionEngine::disable_all(Packet* p) @@ -346,10 +351,10 @@ void DetectionEngine::set_detects(Packet* p, IpsContext::ActiveRules ar) { p->context->active_rules = ar; } void DetectionEngine::set_check_tags(bool enable) -{ Snort::get_switcher()->get_context()->check_tags = enable; } +{ Analyzer::get_switcher()->get_context()->check_tags = enable; } bool DetectionEngine::get_check_tags() -{ return Snort::get_switcher()->get_context()->check_tags; } +{ return Analyzer::get_switcher()->get_context()->check_tags; } //-------------------------------------------------------------------------- // offload / onload @@ -357,7 +362,7 @@ bool DetectionEngine::get_check_tags() bool DetectionEngine::do_offload(Packet* p) { - ContextSwitcher* sw = Snort::get_switcher(); + ContextSwitcher* sw = Analyzer::get_switcher(); assert(p == p->context->packet); assert(p->context == sw->get_context()); @@ -401,7 +406,7 @@ bool DetectionEngine::do_offload(Packet* p) bool DetectionEngine::offload(Packet* p) { - ContextSwitcher* sw = Snort::get_switcher(); + ContextSwitcher* sw = Analyzer::get_switcher(); bool depends_on_suspended = p->flow ? p->flow->context_chain.front() : sw->non_flow_chain.front(); bool can_offload = offloader->available(); @@ -458,14 +463,15 @@ void DetectionEngine::onload(Flow* flow) void DetectionEngine::onload() { - for( Packet* p; offloader->count() and offloader->get(p); ) + Packet* p; + while (offloader->count() and offloader->get(p)) { trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " de::onload %" PRIu64 " (r=%d)\n", p->context->packet_number, p->context->context_num, offloader->count()); p->clear_offloaded(); - IpsContextChain& chain = p->flow ? p->flow->context_chain : Snort::get_switcher()->non_flow_chain; + IpsContextChain& chain = p->flow ? p->flow->context_chain : Analyzer::get_switcher()->non_flow_chain; resume_ready_suspends(chain); } @@ -482,17 +488,25 @@ void DetectionEngine::resume(Packet* p) trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " de::resume %" PRIu64 " (r=%d)\n", p->context->packet_number, p->context->context_num, offloader->count()); - Snort::get_switcher()->resume(p->context); + ContextSwitcher* sw = Analyzer::get_switcher(); + sw->resume(p->context); fp_complete(p); finish_inspect_with_latency(p); // FIXIT-L should latency be evaluated here? finish_inspect(p, true); finish_packet(p); + + if ( !p->is_rebuilt() ) + { + // This happens here to ensure needed contexts are available ASAP as + // not directly forwarding leads to deadlocking waiting on new contexts + Analyzer::get_local_analyzer()->post_process_packet(p); + } } void DetectionEngine::wait_for_context() { - ContextSwitcher* sw = Snort::get_switcher(); + ContextSwitcher* sw = Analyzer::get_switcher(); if ( !sw->idle_count() ) { @@ -576,6 +590,7 @@ bool DetectionEngine::inspect(Packet* p) finish_inspect_with_latency(p); } finish_inspect(p, inspected); + return true; } diff --git a/src/detection/detection_engine.h b/src/detection/detection_engine.h index 07665cb25..c533a25da 100644 --- a/src/detection/detection_engine.h +++ b/src/detection/detection_engine.h @@ -56,6 +56,7 @@ public: static IpsContext* get_context(); static Packet* get_current_packet(); + static Packet* get_current_wire_packet(); static Packet* set_next_packet(Packet* parent = nullptr); static uint8_t* get_next_buffer(unsigned& max); @@ -101,6 +102,8 @@ public: static void set_check_tags(bool enable = true); static bool get_check_tags(); + static void wait_for_context(); + private: static struct SF_EVENTQ* get_event_queue(); static bool do_offload(snort::Packet*); @@ -113,7 +116,6 @@ private: static void finish_inspect_with_latency(Packet*); static void finish_inspect(Packet*, bool inspected); static void finish_packet(Packet*, bool flow_deletion = false); - static void wait_for_context(); private: IpsContext* context; diff --git a/src/detection/detection_util.cc b/src/detection/detection_util.cc index 4bc4060c5..8481fdfba 100644 --- a/src/detection/detection_util.cc +++ b/src/detection/detection_util.cc @@ -86,7 +86,7 @@ void EventTrace_Log(const Packet* p, const OptTreeNode* otn, int action) TextLog_Print(tlog, "Pkt=" STDu64 ", Sec=%lu.%6lu, Len=%u, Cap=%u\n", p->context->packet_number, (long)p->pkth->ts.tv_sec, (long)p->pkth->ts.tv_usec, - p->pkth->pktlen, p->pkth->caplen); + p->pkth->pktlen, p->pktlen); TextLog_Print(tlog, "Pkt Bits: Flags=0x%X, Proto=0x%X, Err=0x%X\n", diff --git a/src/detection/ips_context.h b/src/detection/ips_context.h index a501e13f9..5793699e4 100644 --- a/src/detection/ips_context.h +++ b/src/detection/ips_context.h @@ -135,6 +135,7 @@ public: std::vector rpl; Packet* packet; + Packet* wire_packet; Packet* encode_packet; DAQ_PktHdr_t* pkth; uint8_t* buf; diff --git a/src/detection/tag.cc b/src/detection/tag.cc index 3a299c751..bdca30db0 100644 --- a/src/detection/tag.cc +++ b/src/detection/tag.cc @@ -516,7 +516,7 @@ int CheckTagList(Packet* p, Event& event, void** log_list) if ( returned->metric & TAG_METRIC_BYTES ) { - int n = p->pkth->caplen; + int n = p->pktlen; if ( n < returned->bytes ) returned->bytes -= n; else diff --git a/src/file_api/file_cache.cc b/src/file_api/file_cache.cc index df1670329..3d7a01845 100644 --- a/src/file_api/file_cache.cc +++ b/src/file_api/file_cache.cc @@ -325,8 +325,7 @@ bool FileCache::apply_verdict(Packet* p, FileContext* file_ctx, FileVerdict verd // Won't add packet to retry queue if it is a retransmit // and not from the retry queue since it should already // be there. - if (!(p->packet_flags & PKT_RETRANSMIT) or - p->pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET) + if (!(p->packet_flags & PKT_RETRANSMIT) or p->is_retry()) { PacketTracer::log("File signature lookup: adding packet to retry queue. Resume=%d, Waited %" PRIi64 "ms.\n", resume, time_elapsed_ms(&now, &file_ctx->pending_expire_time, lookup_timeout)); } diff --git a/src/flow/expect_cache.cc b/src/flow/expect_cache.cc index 2e5b968bb..7299374a1 100644 --- a/src/flow/expect_cache.cc +++ b/src/flow/expect_cache.cc @@ -24,7 +24,7 @@ #include "expect_cache.h" #include "hash/zhash.h" -#include "packet_io/sfdaq.h" +#include "packet_io/sfdaq_instance.h" #include "protocols/packet.h" #include "protocols/vlan.h" #include "pub_sub/expect_events.h" @@ -402,7 +402,7 @@ int ExpectCache::add_flow(const Packet *ctrlPkt, last = nullptr; /* Only add TCP and UDP expected flows for now via the DAQ module. */ if (ip_proto == IpProtocol::TCP || ip_proto == IpProtocol::UDP) - SFDAQ::get_local_instance()->add_expected(ctrlPkt, cliIP, cliPort, srvIP, srvPort, + ctrlPkt->daq_instance->add_expected(ctrlPkt, cliIP, cliPort, srvIP, srvPort, ip_proto, 1000, 0); } diff --git a/src/framework/codec.h b/src/framework/codec.h index 18415362c..e23a33b59 100644 --- a/src/framework/codec.h +++ b/src/framework/codec.h @@ -31,7 +31,8 @@ #include "utils/cpp_macros.h" struct TextLog; -struct _daq_pkthdr; +struct _daq_msg; +struct _daq_pkt_hdr; namespace snort { @@ -65,11 +66,13 @@ constexpr uint8_t MAX_TTL = 255; struct RawData { - const _daq_pkthdr* pkth; + const struct _daq_msg* daq_msg; + const _daq_pkt_hdr* pkth; const uint8_t* data; uint32_t len; - RawData(const _daq_pkthdr*, const uint8_t*); + RawData(const struct _daq_msg* daq_msg, const _daq_pkt_hdr* pkth, const uint8_t* data, uint32_t len) : + daq_msg(daq_msg), pkth(pkth), data(data), len(len) { } }; /* Decode Flags */ diff --git a/src/helpers/process.cc b/src/helpers/process.cc index 5663945b4..78d35ef2a 100644 --- a/src/helpers/process.cc +++ b/src/helpers/process.cc @@ -33,7 +33,7 @@ #include "log/messages.h" #include "main.h" -#include "main/snort.h" +#include "main/oops_handler.h" #include "main/snort_config.h" #include "utils/stats.h" #include "utils/util.h" @@ -159,7 +159,7 @@ static void oops_handler(int signal) { // FIXIT-L what should we capture if this is the main thread? if ( !is_main_thread ) - snort::Snort::capture_packet(); + OopsHandler::handle_crash(); add_signal(signal, SIG_DFL, false); raise(signal); diff --git a/src/log/log_text.cc b/src/log/log_text.cc index d4e2b3f77..bba61b109 100644 --- a/src/log/log_text.cc +++ b/src/log/log_text.cc @@ -26,7 +26,7 @@ #include "log_text.h" -#include +#include #include "detection/detection_engine.h" #include "detection/signature.h" @@ -1383,7 +1383,7 @@ void LogPayload(TextLog* log, Packet* p) } else if (SnortConfig::verbose_byte_dump()) { - LogNetData(log, p->pkt, p->pkth->caplen, p); + LogNetData(log, p->pkt, p->pktlen, p); } } } // namespace snort diff --git a/src/loggers/alert_csv.cc b/src/loggers/alert_csv.cc index 778a3acdd..92c4a2085 100644 --- a/src/loggers/alert_csv.cc +++ b/src/loggers/alert_csv.cc @@ -222,7 +222,7 @@ static void ff_icmp_type(Args& a) static void ff_iface(Args&) { - TextLog_Print(csv_log, "%s", SFDAQ::get_interface_spec()); + TextLog_Print(csv_log, "%s", SFDAQ::get_input_spec()); } static void ff_ip_id(Args& a) diff --git a/src/loggers/alert_fast.cc b/src/loggers/alert_fast.cc index 96c5f8b96..39da8279e 100644 --- a/src/loggers/alert_fast.cc +++ b/src/loggers/alert_fast.cc @@ -225,7 +225,7 @@ void FastLogger::alert(Packet* p, const char* msg, const Event& event) event.sig_info->gid, event.sig_info->sid, event.sig_info->rev); if (SnortConfig::alert_interface()) - TextLog_Print(fast_log, " <%s> ", SFDAQ::get_interface_spec()); + TextLog_Print(fast_log, " <%s> ", SFDAQ::get_input_spec()); if ( msg ) TextLog_Puts(fast_log, msg); diff --git a/src/loggers/alert_full.cc b/src/loggers/alert_full.cc index 163ae03ed..5a508b658 100644 --- a/src/loggers/alert_full.cc +++ b/src/loggers/alert_full.cc @@ -157,7 +157,7 @@ void FullLogger::alert(Packet* p, const char* msg, const Event& event) if (SnortConfig::alert_interface()) { - const char* iface = SFDAQ::get_interface_spec(); + const char* iface = SFDAQ::get_input_spec(); TextLog_Print(full_log, " <%s> ", iface); } diff --git a/src/loggers/alert_json.cc b/src/loggers/alert_json.cc index a5ac7dde8..b5cb46123 100644 --- a/src/loggers/alert_json.cc +++ b/src/loggers/alert_json.cc @@ -288,7 +288,7 @@ static bool ff_icmp_type(Args& a) static bool ff_iface(Args& a) { print_label(a, "iface"); - TextLog_Quote(json_log, SFDAQ::get_interface_spec()); + TextLog_Quote(json_log, SFDAQ::get_input_spec()); return true; } diff --git a/src/loggers/alert_syslog.cc b/src/loggers/alert_syslog.cc index 3342b31a3..aa0217f58 100644 --- a/src/loggers/alert_syslog.cc +++ b/src/loggers/alert_syslog.cc @@ -226,7 +226,7 @@ static void AlertSyslog( if (SnortConfig::alert_interface()) { SnortSnprintfAppend(event_string, sizeof(event_string), - "<%s> ", SFDAQ::get_interface_spec()); + "<%s> ", SFDAQ::get_input_spec()); } } if ((p != nullptr) && p->ptrs.ip_api.is_ip()) diff --git a/src/loggers/alert_talos.cc b/src/loggers/alert_talos.cc index db6bff1d7..a5dccd672 100644 --- a/src/loggers/alert_talos.cc +++ b/src/loggers/alert_talos.cc @@ -119,7 +119,7 @@ void TalosLogger::open() { talos_log = new AlertLog; - string ifname = string(SFDAQ::get_interface_spec()); + string ifname = string(SFDAQ::get_input_spec()); size_t sep_pos = ifname.find_last_of("/\\"); if ( sep_pos != string::npos ) diff --git a/src/loggers/alert_unixsock.cc b/src/loggers/alert_unixsock.cc index 2f0ac7644..8e1679da3 100644 --- a/src/loggers/alert_unixsock.cc +++ b/src/loggers/alert_unixsock.cc @@ -135,7 +135,7 @@ static void get_alert_pkt( { us.alert.pkth.ts.tv_sec = (uint32_t)p->pkth->ts.tv_sec; us.alert.pkth.ts.tv_usec = (uint32_t)p->pkth->ts.tv_usec; - us.alert.pkth.caplen = p->pkth->caplen; + us.alert.pkth.caplen = p->pktlen; us.alert.pkth.len = p->pkth->pktlen; memmove(us.alert.pkt, (const void*)p->pkt, us.alert.pkth.caplen); } diff --git a/src/loggers/log_hext.cc b/src/loggers/log_hext.cc index a06a6da10..b3c46c70d 100644 --- a/src/loggers/log_hext.cc +++ b/src/loggers/log_hext.cc @@ -55,8 +55,8 @@ void DaqMetaEventHandler::handle(DataEvent& event, Flow*) const char* cmd; switch (ev->get_type()) { - case DAQ_METAHDR_TYPE_SOF: cmd = "sof"; break; - case DAQ_METAHDR_TYPE_EOF: cmd = "eof"; break; + case DAQ_MSG_TYPE_SOF: cmd = "sof"; break; + case DAQ_MSG_TYPE_EOF: cmd = "eof"; break; default: return; } @@ -106,7 +106,7 @@ void DaqMetaEventHandler::handle(DataEvent& event, Flow*) static void log_raw(const Packet* p) { TextLog_Print(hext_log, "\n# %u [%u]\n", - s_pkt_num++, p->pkth->caplen); + s_pkt_num++, p->pktlen); } static void log_header(const Packet* p) @@ -271,7 +271,7 @@ void HextLogger::log(Packet* p, const char*, Event*) if ( raw ) { log_raw(p); - log_data(p->pkt, p->pkth->caplen, width); + log_data(p->pkt, p->pktlen, width); } else if ( p->has_tcp_data() ) { diff --git a/src/loggers/log_pcap.cc b/src/loggers/log_pcap.cc index 9ad6f8075..b4228f985 100644 --- a/src/loggers/log_pcap.cc +++ b/src/loggers/log_pcap.cc @@ -28,8 +28,9 @@ #include "framework/module.h" #include "log/messages.h" #include "main/snort_config.h" -#include "protocols/packet.h" #include "packet_io/sfdaq.h" +#include "packet_io/sfdaq_config.h" +#include "protocols/packet.h" #include "utils/util.h" using namespace snort; @@ -119,22 +120,22 @@ bool TcpdumpModule::begin(const char*, int, SnortConfig*) // api stuff //------------------------------------------------------------------------- -static inline size_t SizeOf(const DAQ_PktHdr_t* pkth) +static inline size_t SizeOf(const Packet* p) { - return PCAP_PKT_HDR_SZ + pkth->caplen; + return PCAP_PKT_HDR_SZ + p->pktlen; } static void LogTcpdumpSingle( LtdConfig* data, Packet* p, const char*, Event*) { - size_t dumpSize = SizeOf(p->pkth); + size_t dumpSize = SizeOf(p); if ( data->limit && (context.size + dumpSize > data->limit) ) TcpdumpRollLogFile(data); struct pcap_pkthdr pcaphdr; pcaphdr.ts = p->pkth->ts; - pcaphdr.caplen = p->pkth->caplen; + pcaphdr.caplen = p->pktlen; pcaphdr.len = p->pkth->pktlen; pcap_dump((uint8_t*)context.dumpd, &pcaphdr, p->pkt); context.size += dumpSize; @@ -177,7 +178,7 @@ static void TcpdumpInitLogFile(LtdConfig*, bool no_timestamp) dlt = DLT_RAW; pcap_t* pcap; - pcap = pcap_open_dead(dlt, SFDAQ::get_snap_len()); + pcap = pcap_open_dead(dlt, SnortConfig::get_conf()->daq_config->get_mru_size()); if ( !pcap ) FatalError("%s: can't get pcap context\n", S_NAME); diff --git a/src/loggers/unified2.cc b/src/loggers/unified2.cc index 0ba602686..4f2cec023 100644 --- a/src/loggers/unified2.cc +++ b/src/loggers/unified2.cc @@ -359,7 +359,7 @@ static void _Unified2LogPacketAlert( { logheader.packet_second = htonl((uint32_t)p->pkth->ts.tv_sec); logheader.packet_microsecond = htonl((uint32_t)p->pkth->ts.tv_usec); - pkt_length = ( p->is_rebuilt() ) ? p->dsize : p->pkth->caplen; + pkt_length = ( p->is_rebuilt() ) ? p->dsize : p->pktlen; logheader.packet_length = htonl(pkt_length + u2h_len); write_len += pkt_length + u2h_len; } diff --git a/src/main.cc b/src/main.cc index 004c07a4e..c16b8a546 100644 --- a/src/main.cc +++ b/src/main.cc @@ -45,6 +45,8 @@ #include "managers/module_manager.h" #include "managers/plugin_manager.h" #include "packet_io/sfdaq.h" +#include "packet_io/sfdaq_config.h" +#include "packet_io/sfdaq_instance.h" #include "packet_io/trough.h" #include "target_based/sftarget_reader.h" #include "time/periodic.h" @@ -120,30 +122,44 @@ static int current_fd = -1; class Pig { public: - Analyzer* analyzer; - bool awaiting_privilege_change = false; - - Pig() { analyzer = nullptr; athread = nullptr; idx = (unsigned)-1; } + Pig() = default; void set_index(unsigned index) { idx = index; } - void prep(const char* source); + bool prep(const char* source); void start(); void stop(); bool queue_command(AnalyzerCommand*, bool orphan = false); void reap_commands(); + Analyzer* analyzer = nullptr; + bool awaiting_privilege_change = false; + bool requires_privileged_start = true; + private: void reap_command(AnalyzerCommand* ac); - std::thread* athread; - unsigned idx; + std::thread* athread = nullptr; + unsigned idx = (unsigned)-1; }; -void Pig::prep(const char* source) +bool Pig::prep(const char* source) { - analyzer = new Analyzer(idx, source); + SnortConfig* sc = SnortConfig::get_conf(); + SFDAQInstance *instance = new SFDAQInstance(source, sc->daq_config); + if (!SFDAQ::init_instance(instance, sc->bpf_filter)) + { + delete instance; + return false; + } + requires_privileged_start = instance->can_start_unprivileged(); + analyzer = new Analyzer(instance, idx, source, sc->pkt_cnt); + analyzer->set_skip_cnt(sc->pkt_skip); +#ifdef REG_TEST + analyzer->set_pause_after_cnt(sc->pkt_pause_cnt); +#endif + return true; } void Pig::start() @@ -545,15 +561,14 @@ int main_pause(lua_State* L) int main_resume(lua_State* L) { - const bool from_shell = ( L != nullptr ); - - int pkt_num = 0; + bool from_shell = ( L != nullptr ); + uint64_t pkt_num = 0; if (from_shell) { const int num_of_args = lua_gettop(L); if (num_of_args) { - pkt_num = lua_tonumber(L, 1); + pkt_num = lua_tointeger(L, 1); if (pkt_num < 1) { current_request->respond("Invalid usage of resume(n), n should be a number > 0\n"); @@ -714,14 +729,12 @@ static bool just_validate() if ( use_shell(SnortConfig::get_conf()) ) return false; - /* FIXIT-L X This should really check if the DAQ module was unset as it could be explicitly - set to the default value */ - if ( !strcmp(SFDAQ::get_type(), SFDAQ::default_type()) ) + if ( SnortConfig::get_conf()->daq_config->module_configs.empty() ) { if ( SnortConfig::read_mode() && !Trough::get_queue_size() ) return true; - if ( !SnortConfig::read_mode() && !SFDAQ::get_input_spec(SnortConfig::get_conf(), 0) ) + if ( !SnortConfig::read_mode() && !SFDAQ::get_input_spec(SnortConfig::get_conf()->daq_config, 0) ) return true; } @@ -795,7 +808,7 @@ static void handle(Pig& pig, unsigned& swine, unsigned& pending_privileges) break; case Analyzer::State::INITIALIZED: - if (pig.analyzer->requires_privileged_start() && pending_privileges && + if (pig.requires_privileged_start && pending_privileges && !Snort::has_dropped_privileges()) { if (!pig.awaiting_privilege_change) @@ -820,7 +833,7 @@ static void handle(Pig& pig, unsigned& swine, unsigned& pending_privileges) break; case Analyzer::State::STARTED: - if (!pig.analyzer->requires_privileged_start() && pending_privileges && + if (!pig.requires_privileged_start && pending_privileges && !Snort::has_dropped_privileges()) { if (!pig.awaiting_privilege_change) @@ -864,8 +877,11 @@ static void main_loop() // Preemptively prep all pigs in live traffic mode if (!SnortConfig::read_mode()) { - for (swine = 0; swine < max_pigs; swine++) - pigs[swine].prep(SFDAQ::get_input_spec(SnortConfig::get_conf(), swine)); + for (unsigned i = 0; i < max_pigs; i++) + { + if (pigs[i].prep(SFDAQ::get_input_spec(SnortConfig::get_conf()->daq_config, i))) + swine++; + } } // Iterate over the drove, spawn them as allowed, and handle their deaths. @@ -911,8 +927,8 @@ static void main_loop() if ( !exit_requested and (swine < max_pigs) and (src = Trough::get_next()) ) { Pig* pig = get_lazy_pig(max_pigs); - pig->prep(src); - ++swine; + if (pig->prep(src)) + ++swine; continue; } service_check(); diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt index 2dbea9891..fefd44e17 100644 --- a/src/main/CMakeLists.txt +++ b/src/main/CMakeLists.txt @@ -25,6 +25,8 @@ add_library (main OBJECT help.h modules.cc modules.h + oops_handler.cc + oops_handler.h policy.cc request.cc request.h diff --git a/src/main/analyzer.cc b/src/main/analyzer.cc index ebda8a2d1..4b3df8818 100644 --- a/src/main/analyzer.cc +++ b/src/main/analyzer.cc @@ -16,7 +16,7 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- -// analyzer.cc author Russ Combs +// analyzer.cc author Michael Altizer #ifdef HAVE_CONFIG_H #include "config.h" @@ -24,30 +24,394 @@ #include "analyzer.h" +#include + #include +#include "detection/context_switcher.h" +#include "detection/detect.h" +#include "detection/detection_engine.h" +#include "detection/ips_context.h" +#include "detection/tag.h" +#include "file_api/file_service.h" +#include "filters/detection_filter.h" +#include "filters/sfthreshold.h" +#include "flow/ha.h" +#include "framework/data_bus.h" +#include "latency/packet_latency.h" +#include "latency/rule_latency.h" #include "log/messages.h" #include "main/swapper.h" #include "main.h" +#include "managers/action_manager.h" +#include "managers/inspector_manager.h" +#include "managers/ips_manager.h" +#include "managers/event_manager.h" +#include "managers/module_manager.h" +#include "packet_io/active.h" #include "packet_io/sfdaq.h" +#include "packet_io/sfdaq_config.h" +#include "packet_io/sfdaq_instance.h" +#include "packet_tracer/packet_tracer.h" +#include "profiler/profiler.h" +#include "side_channel/side_channel.h" +#include "stream/stream.h" +#include "time/packet_time.h" +#include "utils/stats.h" #include "analyzer_command.h" +#include "oops_handler.h" #include "snort.h" -#include "thread.h" +#include "snort_config.h" +#include "thread_config.h" using namespace snort; using namespace std; -typedef DAQ_Verdict -(* PacketCallback)(void*, const DAQ_PktHdr_t*, const uint8_t*); +static MainHook_f main_hook = snort_ignore; + +THREAD_LOCAL ProfileStats totalPerfStats; +static THREAD_LOCAL Analyzer* local_analyzer = nullptr; + +//------------------------------------------------------------------------- + +class RetryQueue +{ + struct Entry + { + Entry(const struct timeval& next_try, DAQ_Msg_h msg) : next_try(next_try), msg(msg) { } + + struct timeval next_try; + DAQ_Msg_h msg; + }; + +public: + RetryQueue(unsigned interval_ms) + { + assert(interval_ms > 0); + interval = { interval_ms / 1000, (interval_ms % 1000) * 1000 }; + } + + ~RetryQueue() + { + assert(empty()); + } + + void put(DAQ_Msg_h msg) + { + struct timeval now, next_try; + packet_gettimeofday(&now); + timeradd(&now, &interval, &next_try); + queue.emplace_back(next_try, msg); + } + + DAQ_Msg_h get(const struct timeval* now = nullptr) + { + if (!empty()) + { + const Entry& entry = queue.front(); + if (!now || !timercmp(now, &entry.next_try, <)) + { + DAQ_Msg_h msg = entry.msg; + queue.pop_front(); + return msg; + } + } + return nullptr; + } + + bool empty() const + { + return queue.empty(); + } + +private: + deque queue; + struct timeval interval; +}; + +//------------------------------------------------------------------------- + +/* + * Static Class Methods + */ +Analyzer* Analyzer::get_local_analyzer() +{ + return local_analyzer; +} + +ContextSwitcher* Analyzer::get_switcher() +{ + assert(local_analyzer != nullptr); + return local_analyzer->switcher; +} + +void Analyzer::set_main_hook(MainHook_f f) +{ + main_hook = f; +} + +//------------------------------------------------------------------------- +// message processing +//------------------------------------------------------------------------- + +static void process_daq_sof_eof_msg(DAQ_Msg_h msg) +{ + const Flow_Stats_t *stats = (const Flow_Stats_t *) daq_msg_get_hdr(msg); + + if (msg->type == DAQ_MSG_TYPE_EOF) + packet_time_update(&stats->eof_timestamp); + else + packet_time_update(&stats->sof_timestamp); + + DataBus::publish(DAQ_META_EVENT, nullptr, daq_msg_get_type(msg), (const uint8_t*) stats); +} + +static bool process_packet(Packet* p) +{ + assert(p->pkth && p->pkt); + + aux_counts.rx_bytes += p->pktlen; + + PacketTracer::activate(*p); + + // FIXIT-M should not need to set policies here + set_default_policy(); + p->user_inspection_policy_id = get_inspection_policy()->user_policy_id; + p->user_ips_policy_id = get_ips_policy()->user_policy_id; + p->user_network_policy_id = get_network_policy()->user_policy_id; + + if ( !(p->packet_flags & PKT_IGNORE) ) + { + clear_file_data(); + // return incomplete status if the main hook indicates not all work was done + if (!main_hook(p)) + return false; + } + + return true; +} + +// Finalize DAQ message verdict +static DAQ_Verdict distill_verdict(Packet* p) +{ + DAQ_Verdict verdict = DAQ_VERDICT_PASS; + Active* act = p->active; + + // First Pass + if ( act->packet_retry_requested() ) + { + verdict = DAQ_VERDICT_RETRY; + } + else if ( act->session_was_blocked() ) + { + if ( !act->can_block() ) + verdict = DAQ_VERDICT_PASS; + else if ( act->get_tunnel_bypass() ) + { + aux_counts.internal_blacklist++; + verdict = DAQ_VERDICT_BLOCK; + } + else if ( SnortConfig::inline_mode() || act->packet_force_dropped() ) + verdict = DAQ_VERDICT_BLACKLIST; + else + verdict = DAQ_VERDICT_IGNORE; + } + + // Second Pass, now with more side effects + if ( act->packet_was_dropped() && act->can_block() ) + { + if ( verdict == DAQ_VERDICT_PASS ) + verdict = DAQ_VERDICT_BLOCK; + } + else if ( verdict == DAQ_VERDICT_RETRY ) + { + } + else if ( p->packet_flags & PKT_RESIZED ) + { + // we never increase, only trim, but daq doesn't support resizing wire packet + PacketManager::encode_update(p); + + if ( p->daq_instance->inject(p->daq_msg, 0, p->pkt, p->pktlen) == DAQ_SUCCESS ) + verdict = DAQ_VERDICT_BLOCK; + // FIXIT-M X Should we be blocking the wire packet even if the injection fails? + } + else if ( p->packet_flags & PKT_MODIFIED ) + { + // this packet was normalized and/or has replacements + PacketManager::encode_update(p); + verdict = DAQ_VERDICT_REPLACE; + } + else if ( (p->packet_flags & PKT_IGNORE) || + (p->flow && p->flow->get_ignore_direction() == SSN_DIR_BOTH) ) + { + if ( !act->get_tunnel_bypass() ) + { + verdict = DAQ_VERDICT_WHITELIST; + } + else + { + verdict = DAQ_VERDICT_PASS; + aux_counts.internal_whitelist++; + } + } + else if ( p->ptrs.decode_flags & DECODE_PKT_TRUST ) + { + if (p->flow) + p->flow->set_ignore_direction(SSN_DIR_BOTH); + verdict = DAQ_VERDICT_WHITELIST; + } + else + verdict = DAQ_VERDICT_PASS; + + return verdict; +} + +/* + * Private message processing methods + */ +void Analyzer::post_process_daq_pkt_msg(Packet* p) +{ + ActionManager::execute(p); + + DAQ_Verdict verdict = distill_verdict(p); + + if (PacketTracer::is_active()) + { + PacketTracer::log("Policies: Network %u, Inspection %u, Detection %u\n", + get_network_policy()->user_policy_id, get_inspection_policy()->user_policy_id, + get_ips_policy()->user_policy_id); + PacketTracer::log("Verdict: %s\n", SFDAQ::verdict_to_string(verdict)); + + PacketTracer::dump(p); + } + + HighAvailabilityManager::process_update(p->flow, p->pkth); + + p->pkth = nullptr; // no longer avail upon sig segv + + if (verdict == DAQ_VERDICT_RETRY) + retry_queue->put(p->daq_msg); + else + p->daq_instance->finalize_message(p->daq_msg, verdict); +} + +void Analyzer::process_daq_pkt_msg(DAQ_Msg_h msg, bool retry) +{ + const DAQ_PktHdr_t* pkthdr = daq_msg_get_pkthdr(msg); + + set_default_policy(); + Profile profile(totalPerfStats); + + if (!retry) + { + pc.total_from_daq++; + packet_time_update(&pkthdr->ts); + } + + DetectionEngine::wait_for_context(); + switcher->start(); + Packet* p = switcher->get_context()->packet; + oops_handler->set_current_packet(p); + p->context->wire_packet = p; + p->context->packet_number = pc.total_from_daq; + + DetectionEngine::reset(); + + sfthreshold_reset(); + ActionManager::reset_queue(p); + + p->daq_msg = msg; + p->daq_instance = daq_instance; + PacketManager::decode(p, pkthdr, daq_msg_get_data(msg), daq_msg_get_data_len(msg), false, retry); + if (process_packet(p)) + { + post_process_daq_pkt_msg(p); + switcher->stop(); + } + + Stream::timeout_flows(packet_time()); + HighAvailabilityManager::process_receive(); +} + +void Analyzer::process_daq_msg(DAQ_Msg_h msg, bool retry) +{ + switch (daq_msg_get_type(msg)) + { + case DAQ_MSG_TYPE_PACKET: + process_daq_pkt_msg(msg, retry); + // process_daq_pkt_msg() handles finalizing the message (or tracking it if offloaded) + return; + case DAQ_MSG_TYPE_SOF: + case DAQ_MSG_TYPE_EOF: + process_daq_sof_eof_msg(msg); + break; + default: + break; + } + daq_instance->finalize_message(msg, DAQ_VERDICT_PASS); +} + +void Analyzer::process_retry_queue() +{ + if (!retry_queue->empty()) + { + struct timeval now; + packet_gettimeofday(&now); + DAQ_Msg_h msg; + while ((msg = retry_queue->get(&now)) != nullptr) + process_daq_msg(msg, true); + } +} + +/* + * Public packet processing methods + */ +bool Analyzer::inspect_rebuilt(Packet* p) +{ + // Need to include this b/c call is outside the detect tree + Profile detect_profile(detectPerfStats); + DeepProfile rebuilt_profile(rebuiltPacketPerfStats); + + DetectionEngine de; + return main_hook(p); +} -// FIXIT-M add fail open capability -static THREAD_LOCAL PacketCallback main_func = Snort::packet_callback; +bool Analyzer::process_rebuilt_packet(Packet* p, const DAQ_PktHdr_t* pkthdr, const uint8_t* pkt, uint32_t pktlen) +{ + PacketManager::decode(p, pkthdr, pkt, pktlen, true); + + p->packet_flags |= (PKT_PSEUDO | PKT_REBUILT_FRAG); + p->pseudo_type = PSEUDO_PKT_IP; + + return process_packet(p); +} + +void Analyzer::post_process_packet(Packet* p) +{ + post_process_daq_pkt_msg(p); + // FIXIT-? There is an assumption that this is being called on the active context... + switcher->stop(); +} //------------------------------------------------------------------------- -// analyzer +// Utility //------------------------------------------------------------------------- +void Analyzer::show_source() +{ + const char* pcap = source.c_str(); + + if (!strcmp(pcap, "-")) + pcap = "stdin"; + + if (get_run_num() != 1) + fprintf(stdout, "%s", "\n"); + + fprintf(stdout, "Reading network traffic from \"%s\" with snaplen = %u\n", + pcap, SnortConfig::get_conf()->daq_config->get_mru_size()); +} + void Analyzer::set_state(State s) { state = s; @@ -60,47 +424,174 @@ const char* Analyzer::get_state_string() switch ( s ) { - case State::NEW: return "NEW"; - case State::INITIALIZED: return "INITIALIZED"; - case State::STARTED: return "STARTED"; - case State::RUNNING: return "RUNNING"; - case State::PAUSED: return "PAUSED"; - case State::STOPPED: return "STOPPED"; - default: assert(false); + case State::NEW: return "NEW"; + case State::INITIALIZED: return "INITIALIZED"; + case State::STARTED: return "STARTED"; + case State::RUNNING: return "RUNNING"; + case State::PAUSED: return "PAUSED"; + case State::STOPPED: return "STOPPED"; + default: assert(false); } return "UNKNOWN"; } -Analyzer::Analyzer(unsigned i, const char* s) +//------------------------------------------------------------------------- +// Thread life cycle +//------------------------------------------------------------------------- + +void Analyzer::idle() +{ + // FIXIT-L this whole thing could be pub-sub + DataBus::publish(THREAD_IDLE_EVENT, nullptr); + if (SnortConfig::read_mode()) + Stream::timeout_flows(packet_time()); + else + Stream::timeout_flows(time(nullptr)); + aux_counts.idle++; + HighAvailabilityManager::process_receive(); +} + +/* + * Perform all packet thread initialization actions that can be taken with dropped privileges + * and/or must be called after the DAQ module has been started. + */ +void Analyzer::init_unprivileged() +{ + // using dummy values until further integration + const unsigned max_contexts = 20; + + switcher = new ContextSwitcher; + + for ( unsigned i = 0; i < max_contexts; ++i ) + switcher->push(new IpsContext); + + SnortConfig* sc = SnortConfig::get_conf(); + + CodecManager::thread_init(sc); + + // this depends on instantiated daq capabilities + // so it is done here instead of init() + Active::thread_init(sc); + + InitTag(); + EventTrace_Init(); + detection_filter_init(sc->detection_filter_config); + DetectionEngine::thread_init(); + + EventManager::open_outputs(); + IpsManager::setup_options(); + ActionManager::thread_init(sc); + FileService::thread_init(); + SideChannelManager::thread_init(); + HighAvailabilityManager::thread_init(); // must be before InspectorManager::thread_init(); + InspectorManager::thread_init(sc); + PacketTracer::thread_init(); + + // in case there are HA messages waiting, process them first + HighAvailabilityManager::process_receive(); + PacketManager::thread_init(); +} + +void Analyzer::reinit(SnortConfig* sc) +{ + InspectorManager::thread_reinit(sc); + ActionManager::thread_reinit(sc); +} + +void Analyzer::term() +{ + SnortConfig* sc = SnortConfig::get_conf(); + + HighAvailabilityManager::thread_term_beginning(); + + if ( !sc->dirty_pig ) + Stream::purge_flows(); + + DetectionEngine::idle(); + InspectorManager::thread_stop(sc); + ModuleManager::accumulate(sc); + InspectorManager::thread_term(sc); + ActionManager::thread_term(sc); + + IpsManager::clear_options(); + EventManager::close_outputs(); + CodecManager::thread_term(); + HighAvailabilityManager::thread_term(); + SideChannelManager::thread_term(); + + oops_handler->set_current_packet(nullptr); + + if ( daq_instance->was_started() ) + { + DAQ_Msg_h msg; + while ((msg = retry_queue->get()) != nullptr) + daq_instance->finalize_message(msg, DAQ_VERDICT_BLOCK); + daq_instance->stop(); + } + SFDAQ::set_local_instance(nullptr); + + PacketLatency::tterm(); + RuleLatency::tterm(); + + Profiler::consolidate_stats(); + + DetectionEngine::thread_term(); + detection_filter_term(); + EventTrace_Term(); + CleanupTag(); + FileService::thread_term(); + PacketTracer::thread_term(); + PacketManager::thread_term(); + + Active::thread_term(); + delete switcher; +} + +Analyzer::Analyzer(SFDAQInstance* instance, unsigned i, const char* s, uint64_t msg_cnt) { id = i; + exit_after_cnt = msg_cnt; source = s ? s : ""; - daq_instance = nullptr; - privileged_start = false; - exit_requested = false; + daq_instance = instance; + retry_queue = new RetryQueue(200); set_state(State::NEW); } +Analyzer::~Analyzer() +{ + delete daq_instance; + delete oops_handler; + delete retry_queue; +} + void Analyzer::operator()(Swapper* ps, uint16_t run_num) { + oops_handler = new OopsHandler(); + set_thread_type(STHREAD_TYPE_PACKET); set_instance_id(id); set_run_num(run_num); + local_analyzer = this; - ps->apply(); + ps->apply(*this); delete ps; - if (Snort::thread_init_privileged(source.c_str())) - { - daq_instance = SFDAQ::get_local_instance(); - privileged_start = daq_instance->can_start_unprivileged(); - set_state(State::INITIALIZED); + if (SnortConfig::pcap_show()) + show_source(); - analyze(); + // Perform all packet thread initialization actions that need to be taken with escalated + // privileges prior to starting the DAQ module. + SnortConfig::get_conf()->thread_config->implement_thread_affinity(STHREAD_TYPE_PACKET, + get_instance_id()); - Snort::thread_term(); - } + SFDAQ::set_local_instance(daq_instance); + set_state(State::INITIALIZED); + + // Start the main loop + analyze(); + + term(); set_state(State::STOPPED); } @@ -115,8 +606,8 @@ void Analyzer::execute(AnalyzerCommand* ac) /* Break out of the DAQ acquire loop so that the command will be processed. This is explicitly safe to call from another thread. */ - if ( state == State::RUNNING and daq_instance ) - daq_instance->break_loop(0); + if ( state >= State::STARTED and state < State::STOPPED and daq_instance ) + daq_instance->interrupt(); } bool Analyzer::handle_command() @@ -143,38 +634,100 @@ bool Analyzer::handle_command() return true; } -void Analyzer::analyze() +void Analyzer::handle_commands() { - // The main analyzer loop is terminated by a command returning false or an error during acquire - while (!exit_requested) + while (handle_command()) + ; +} + +DAQ_RecvStatus Analyzer::process_messages() +{ + // Max receive becomes the minimum of the configured batch size, the remaining exit_after + // count (if requested), and the remaining pause_after count (if requested). + unsigned max_recv = daq_instance->get_batch_size(); + if (exit_after_cnt && exit_after_cnt < max_recv) + max_recv = exit_after_cnt; + if (pause_after_cnt && pause_after_cnt < max_recv) + max_recv = pause_after_cnt; + + DAQ_RecvStatus rstat = daq_instance->receive_messages(max_recv); + + unsigned num_recv = 0; + DAQ_Msg_h msg; + while ((msg = daq_instance->next_message()) != nullptr) { - TestPause& s_pause = Snort::get_test_pause(); - if (s_pause.get_pause()) + // Dispose of any messages to be skipped first. + if (skip_cnt > 0) { - pause(); - s_pause.clear_pause(); - s_pause.set_pause_cnt(0); - snort::LogMessage("== paused\n"); - } - if (handle_command()) + aux_counts.skipped++; + skip_cnt--; + daq_instance->finalize_message(msg, DAQ_VERDICT_PASS); continue; + } + // FIXIT-M add fail open capability + // IMPORTANT: process_daq_msg() is responsible for finalizing the messages. + num_recv++; + process_daq_msg(msg, false); + DetectionEngine::onload(); + process_retry_queue(); + } + if (exit_after_cnt && (exit_after_cnt -= num_recv) == 0) + stop(); + if (pause_after_cnt && (pause_after_cnt -= num_recv) == 0) + pause(); + return rstat; +} + +void Analyzer::analyze() +{ + while (!exit_requested) + { // If we're not in the running state (usually either pre-start or paused), // just keep stalling until something else comes up. if (state != State::RUNNING) { - chrono::milliseconds ms(10); - this_thread::sleep_for(ms); + if (!handle_command()) + { + chrono::milliseconds ms(10); + this_thread::sleep_for(ms); + } continue; } - if (daq_instance->acquire(0, main_func)) - break; - // FIXIT-L acquire(0) makes idle processing unlikely under high traffic - // because it won't return until no packets, signal, etc. that means - // the idle processing may not be useful or that we need a hook to do - // things periodically even when traffic is available - Snort::thread_idle(); + // Receive and process a batch of messages. Evaluate the receive status after processing + // the returned messages to determine if we should immediately continue, take the opportunity + // to deal with some house cleaning work, or terminate the analyzer thread. + DAQ_RecvStatus rstat = process_messages(); + if (rstat != DAQ_RSTAT_OK && rstat != DAQ_RSTAT_WOULD_BLOCK) + { + if (rstat == DAQ_RSTAT_TIMEOUT) + { + // If the receive timed out, let's do some idle work before continuing. + // FIXIT-L Hitting a one second timeout when attached to any real traffic source + // is extremely unlikely, so relying on anything in thread_idle() ever being + // called is dangerous. + idle(); + } + else if (rstat == DAQ_RSTAT_INTERRUPTED) + { + // If the status reports INTERRUPTED because of an interrupt() call, exit_requested should + // be set for the next pass through the main loop. Use this as a hint to check for analyzer + // commands. + handle_commands(); + } + else + { + if (rstat == DAQ_RSTAT_NOBUF) + ErrorMessage("Exhausted the DAQ message pool!\n"); + else if (rstat == DAQ_RSTAT_ERROR) + ErrorMessage("Error receiving message from the DAQ instance: %s\n", daq_instance->get_error()); + // Implicitly handled: + // DAQ_RSTAT_EOF - File readback completed, job well done; let's get out of here. + // DAQ_RSTAT_INVALID - This really shouldn't happen. + break; + } + } } } @@ -193,7 +746,7 @@ void Analyzer::start() void Analyzer::run(bool paused) { assert(state == State::STARTED); - Snort::thread_init_unprivileged(); + init_unprivileged(); if ( paused ) set_state(State::PAUSED); else @@ -217,12 +770,11 @@ void Analyzer::pause() get_state_string()); } -void Analyzer::resume(int pkt_cnt) +void Analyzer::resume(uint64_t msg_cnt) { if (state == State::PAUSED) { - TestPause& s_pause = Snort::get_test_pause(); - s_pause.set_pause_cnt(pkt_cnt); + set_pause_after_cnt(msg_cnt); set_state(State::RUNNING); } else @@ -236,3 +788,8 @@ void Analyzer::reload_daq() daq_instance->reload(); } +void Analyzer::rotate() +{ + DataBus::publish(THREAD_ROTATE_EVENT, nullptr); +} + diff --git a/src/main/analyzer.h b/src/main/analyzer.h index 9fb6a552e..b8a4c6670 100644 --- a/src/main/analyzer.h +++ b/src/main/analyzer.h @@ -15,7 +15,7 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- -// analyzer.h author Russ Combs +// analyzer.h author Michael Altizer #ifndef ANALYZER_H #define ANALYZER_H @@ -24,19 +24,31 @@ // runs in a different thread, it also provides a command facility so that // to control the thread and swap configuration. +#include + #include #include #include #include +#include "thread.h" + class AnalyzerCommand; +class ContextSwitcher; +class OopsHandler; +class RetryQueue; class Swapper; namespace snort { class SFDAQInstance; +struct Packet; +struct SnortConfig; +struct ProfileStats; } +typedef bool (* MainHook_f)(snort::Packet*); + class Analyzer { public: @@ -49,7 +61,13 @@ public: STOPPED, NUM_STATES }; - Analyzer(unsigned id, const char* source); + + static Analyzer* get_local_analyzer(); + static ContextSwitcher* get_switcher(); + static void set_main_hook(MainHook_f); + + Analyzer(snort::SFDAQInstance*, unsigned id, const char* source, uint64_t msg_cnt = 0); + ~Analyzer(); void operator()(Swapper*, uint16_t run_num); @@ -57,22 +75,40 @@ public: const char* get_state_string(); const char* get_source() { return source.c_str(); } + void set_pause_after_cnt(uint64_t msg_cnt) { pause_after_cnt = msg_cnt; } + void set_skip_cnt(uint64_t msg_cnt) { skip_cnt = msg_cnt; } + void execute(AnalyzerCommand*); - bool requires_privileged_start() { return privileged_start; } + void post_process_packet(snort::Packet*); + bool process_rebuilt_packet(snort::Packet*, const DAQ_PktHdr_t*, const uint8_t* pkt, uint32_t pktlen); + bool inspect_rebuilt(snort::Packet*); // Functions called by analyzer commands void start(); void run(bool paused = false); void stop(); void pause(); - void resume(int pkt_cnt); + void resume(uint64_t msg_cnt); void reload_daq(); + void reinit(snort::SnortConfig*); + void rotate(); private: void analyze(); bool handle_command(); + void handle_commands(); + DAQ_RecvStatus process_messages(); + void process_daq_msg(DAQ_Msg_h, bool retry); + void process_daq_pkt_msg(DAQ_Msg_h, bool retry); + void post_process_daq_pkt_msg(snort::Packet*); + void process_retry_queue(); void set_state(State); + void idle(); + bool init_privileged(); + void init_unprivileged(); + void term(); + void show_source(); public: std::queue completed_work_queue; @@ -81,16 +117,24 @@ public: private: std::atomic state; - std::atomic privileged_start; unsigned id; - bool exit_requested; + bool exit_requested = false; + + uint64_t exit_after_cnt; + uint64_t pause_after_cnt = 0; + uint64_t skip_cnt = 0; std::string source; snort::SFDAQInstance* daq_instance; + RetryQueue* retry_queue = nullptr; + OopsHandler* oops_handler = nullptr; + ContextSwitcher* switcher = nullptr; std::mutex pending_work_queue_mutex; }; +extern THREAD_LOCAL snort::ProfileStats totalPerfStats; + #endif diff --git a/src/main/analyzer_command.cc b/src/main/analyzer_command.cc index 479f12283..ae2323bba 100644 --- a/src/main/analyzer_command.cc +++ b/src/main/analyzer_command.cc @@ -58,12 +58,12 @@ void ACPause::execute(Analyzer& analyzer) void ACResume::execute(Analyzer& analyzer) { - analyzer.resume(pkt_count); + analyzer.resume(msg_cnt); } -void ACRotate::execute(Analyzer&) +void ACRotate::execute(Analyzer& analyzer) { - snort::Snort::thread_rotate(); + analyzer.rotate(); } void ACGetStats::execute(Analyzer&) @@ -89,10 +89,10 @@ ACSwap::ACSwap(Swapper* ps, Request* req, bool from_shell) : ps(ps), request(req Swapper::set_reload_in_progress(true); } -void ACSwap::execute(Analyzer&) +void ACSwap::execute(Analyzer& analyzer) { if (ps) - ps->apply(); + ps->apply(analyzer); } ACSwap::~ACSwap() diff --git a/src/main/analyzer_command.h b/src/main/analyzer_command.h index 79da9151e..ea91dd08b 100644 --- a/src/main/analyzer_command.h +++ b/src/main/analyzer_command.h @@ -56,11 +56,11 @@ public: class ACResume : public AnalyzerCommand { public: - ACResume(int n): pkt_count(n){} + ACResume(uint64_t msg_cnt): msg_cnt(msg_cnt) { } void execute(Analyzer&) override; const char* stringify() override { return "RESUME"; } private: - int pkt_count; + uint64_t msg_cnt; }; class ACRotate : public AnalyzerCommand diff --git a/src/main/help.cc b/src/main/help.cc index 2fae1613b..fb069049d 100644 --- a/src/main/help.cc +++ b/src/main/help.cc @@ -280,7 +280,7 @@ void config_markup(SnortConfig*, const char*) [[noreturn]] void list_daqs(SnortConfig* sc) { - SFDAQ::load(sc); + SFDAQ::load(sc->daq_config); SFDAQ::print_types(cout); SFDAQ::unload(); exit(0); diff --git a/src/main/modules.cc b/src/main/modules.cc old mode 100755 new mode 100644 diff --git a/src/main/oops_handler.cc b/src/main/oops_handler.cc new file mode 100644 index 000000000..59af4eff5 --- /dev/null +++ b/src/main/oops_handler.cc @@ -0,0 +1,60 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// oops_handler.cc author Michael Altizer + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "oops_handler.h" + +#include "protocols/packet.h" + +static THREAD_LOCAL OopsHandler* local_oops_handler = nullptr; + +void OopsHandler::handle_crash() +{ + if (local_oops_handler) + local_oops_handler->eternalize(); +} + +OopsHandler::OopsHandler() +{ + assert(local_oops_handler == nullptr); + local_oops_handler = this; +} + +OopsHandler::~OopsHandler() +{ + local_oops_handler = nullptr; +} + +void OopsHandler::eternalize() +{ + // Copy the crashed thread's data. C++11 specs ensure the + // thread that segfaulted will still be running. + if (packet && packet->pkth) + { + pkth = *(packet->pkth); + if (packet->pkt) + { + memcpy(data, packet->pkt, 0xFFFF & packet->pktlen); + packet->pkt = data; + } + } +} diff --git a/src/main/oops_handler.h b/src/main/oops_handler.h new file mode 100644 index 000000000..ceda70831 --- /dev/null +++ b/src/main/oops_handler.h @@ -0,0 +1,47 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// oops_handler.h author Michael Altizer + +#ifndef OOPS_HANDLER_H +#define OOPS_HANDLER_H + +#include + +namespace snort +{ +struct Packet; +} + +class OopsHandler +{ +public: + static void handle_crash(); + + OopsHandler(); + ~OopsHandler(); + void set_current_packet(snort::Packet* p) { packet = p; } + void eternalize(); + +private: + DAQ_PktHdr_t pkth = { }; + uint8_t data[65535] = { }; + snort::Packet* packet = nullptr; +}; + +#endif + diff --git a/src/main/snort.cc b/src/main/snort.cc index fe8e4397f..922231401 100644 --- a/src/main/snort.cc +++ b/src/main/snort.cc @@ -23,42 +23,29 @@ #include "snort.h" +#include #include #include #include "actions/ips_actions.h" #include "codecs/codec_api.h" #include "connectors/connectors.h" -#include "decompress/file_decomp.h" -#include "detection/context_switcher.h" #include "detection/detect.h" -#include "detection/detection_engine.h" -#include "detection/detection_util.h" #include "detection/fp_config.h" #include "detection/fp_detect.h" -#include "detection/ips_context.h" -#include "detection/tag.h" #include "file_api/file_service.h" -#include "filters/detection_filter.h" #include "filters/rate_filter.h" #include "filters/sfthreshold.h" #include "flow/ha.h" -#include "framework/endianness.h" #include "framework/mpse.h" -#include "helpers/base64_encoder.h" #include "helpers/process.h" #include "host_tracker/host_cache.h" -#include "ips_options/ips_flowbits.h" #include "ips_options/ips_options.h" -#include "latency/packet_latency.h" -#include "latency/rule_latency.h" #include "log/log.h" #include "log/messages.h" #include "loggers/loggers.h" #include "main.h" #include "main/shell.h" -#include "main/thread_config.h" -#include "managers/action_manager.h" #include "managers/codec_manager.h" #include "managers/inspector_manager.h" #include "managers/ips_manager.h" @@ -72,21 +59,16 @@ #include "packet_io/active.h" #include "packet_io/sfdaq.h" #include "packet_io/trough.h" -#include "packet_tracer/packet_tracer.h" #include "parser/cmd_line.h" #include "parser/parser.h" #include "profiler/profiler.h" #include "search_engines/search_engines.h" #include "service_inspectors/service_inspectors.h" #include "side_channel/side_channel.h" -#include "stream/stream.h" #include "stream/stream_inspectors.h" #include "target_based/sftarget_reader.h" -#include "time/packet_time.h" #include "time/periodic.h" #include "utils/util.h" -#include "utils/util_utf.h" -#include "utils/util_jsnorm.h" #ifdef PIGLET #include "piglet/piglet.h" @@ -106,37 +88,14 @@ using namespace snort; using namespace std; -//------------------------------------------------------------------------- - static SnortConfig* snort_cmd_line_conf = nullptr; static pid_t snort_main_thread_pid = 0; -// non-local for easy access from core -static THREAD_LOCAL DAQ_PktHdr_t s_pkth; -static THREAD_LOCAL uint8_t* s_data = nullptr; -static THREAD_LOCAL Packet* s_packet = nullptr; -static THREAD_LOCAL ContextSwitcher* s_switcher = nullptr; - -ContextSwitcher* Snort::get_switcher() -{ return s_switcher; } - -// Test util - used for pause-after-n and resume(n) -static THREAD_LOCAL TestPause s_pause; - -TestPause& Snort::get_test_pause() -{ return s_pause; } - -void TestPause::set_pause_cnt(int cnt) -{ pause_cnt = cnt ? (cnt + pc.total_from_daq) : 0; } - //------------------------------------------------------------------------- // perf stats // FIXIT-M move these to appropriate modules //------------------------------------------------------------------------- -static THREAD_LOCAL ProfileStats totalPerfStats; -static THREAD_LOCAL ProfileStats metaPerfStats; - static ProfileStats* get_profile(const char* key) { if ( !strcmp(key, "detect") ) @@ -169,9 +128,6 @@ static ProfileStats* get_profile(const char* key) if ( !strcmp(key, "total") ) return &totalPerfStats; - if ( !strcmp(key, "daq_meta") ) - return &metaPerfStats; - return nullptr; } @@ -194,36 +150,6 @@ static void register_profiles() // helpers //------------------------------------------------------------------------- -static bool pass_pkts(Packet*) { return true; } -static MainHook_f main_hook = pass_pkts; - -static void set_policy(Packet* p) -{ - set_default_policy(); - p->user_inspection_policy_id = get_inspection_policy()->user_policy_id; - p->user_ips_policy_id = get_ips_policy()->user_policy_id; - p->user_network_policy_id = get_network_policy()->user_policy_id; -} - -static void show_source(const char* pcap) -{ - if ( !SnortConfig::pcap_show() ) - return; - - if ( !strcmp(pcap, "-") ) - pcap = "stdin"; - - static bool first = true; - if ( first ) - first = false; - else - fprintf(stdout, "%s", "\n"); - - fprintf(stdout, "Reading network traffic from \"%s\" with snaplen = %u\n", - pcap, SFDAQ::get_snap_len()); -} - - //------------------------------------------------------------------------- // initialization //------------------------------------------------------------------------- @@ -273,7 +199,7 @@ void Snort::init(int argc, char** argv) ScriptManager::load_scripts(snort_cmd_line_conf->script_paths); PluginManager::load_plugins(snort_cmd_line_conf->plugin_path); - if ( SnortConfig::get_conf()->logging_flags & LOGGING_FLAG__SHOW_PLUGINS ) + if ( snort_cmd_line_conf->logging_flags & LOGGING_FLAG__SHOW_PLUGINS ) { ModuleManager::dump_modules(); PluginManager::dump_plugins(); @@ -352,6 +278,10 @@ void Snort::init(int argc, char** argv) parser_term(sc); Active::init(sc); + + LogMessage("%s\n", LOG_DIV); + + SFDAQ::init(sc->daq_config); } // this function should only include initialization that must be done as a @@ -380,10 +310,9 @@ bool Snort::drop_privileges() /* Drop privileges if requested. */ if (SnortConfig::get_uid() != -1 || SnortConfig::get_gid() != -1) { - if (!SFDAQ::unprivileged()) + if (!SFDAQ::can_run_unprivileged()) { - ParseError("Cannot drop privileges - %s DAQ does not support unprivileged operation.\n", - SFDAQ::get_type()); + ParseError("Cannot drop privileges - at least one of the configured DAQ modules does not support unprivileged operation.\n"); return false; } if (!SetUidGid(SnortConfig::get_uid(), SnortConfig::get_gid())) @@ -509,12 +438,6 @@ bool Snort::is_reloading() bool Snort::has_dropped_privileges() { return privileges_dropped; } -void Snort::set_main_hook(MainHook_f f) -{ main_hook = f; } - -Packet* Snort::get_packet() -{ return s_packet; } - void Snort::setup(int argc, char* argv[]) { set_main_thread(); @@ -525,9 +448,6 @@ void Snort::setup(int argc, char* argv[]) init(argc, argv); - LogMessage("%s\n", LOG_DIV); - SFDAQ::init(SnortConfig::get_conf()); - if ( SnortConfig::daemon_mode() ) daemonize(); @@ -737,341 +657,3 @@ SnortConfig* Snort::get_updated_module(SnortConfig* other_conf, const char* name return sc; } -void Snort::capture_packet() -{ - if ( snort_main_thread_pid == gettid() ) - { - // FIXIT-L main thread crashed. Do anything? - } - else - { - // Copy the crashed threads data. C++11 specs ensure the - // thread that segfaulted will still be running. - if ( s_packet && s_packet->pkth ) - { - s_pkth = *(s_packet->pkth); - - if ( s_packet->pkt ) - { - memcpy(s_data, s_packet->pkt, 0xFFFF & s_packet->pkth->caplen); - s_packet->pkt = s_data; - } - } - } -} - -void Snort::thread_idle() -{ - // FIXIT-L this whole thing could be pub-sub - DataBus::publish(THREAD_IDLE_EVENT, nullptr); - if (SnortConfig::read_mode()) - Stream::timeout_flows(packet_time()); - else - Stream::timeout_flows(time(nullptr)); - aux_counts.idle++; - HighAvailabilityManager::process_receive(); -} - -void Snort::thread_rotate() -{ - DataBus::publish(THREAD_ROTATE_EVENT, nullptr); -} - -/* - * Perform all packet thread initialization actions that need to be taken with escalated privileges - * prior to starting the DAQ module. - */ -bool Snort::thread_init_privileged(const char* intf) -{ - s_data = new uint8_t[65535]; - show_source(intf); - - SnortConfig::get_conf()->thread_config->implement_thread_affinity(STHREAD_TYPE_PACKET, - get_instance_id()); - - // FIXIT-M the start-up sequence is a little off due to dropping privs - SFDAQInstance* daq_instance = new SFDAQInstance(intf); - SFDAQ::set_local_instance(daq_instance); - if (!daq_instance->configure(SnortConfig::get_conf())) - { - SFDAQ::set_local_instance(nullptr); - delete daq_instance; - return false; - } - - return true; -} - -/* - * Perform all packet thread initialization actions that can be taken with dropped privileges - * and/or must be called after the DAQ module has been started. - */ -void Snort::thread_init_unprivileged() -{ - // using dummy values until further integration - const unsigned max_contexts = 20; - - s_switcher = new ContextSwitcher; - - for ( unsigned i = 0; i < max_contexts; ++i ) - s_switcher->push(new IpsContext); - - CodecManager::thread_init(SnortConfig::get_conf()); - - // this depends on instantiated daq capabilities - // so it is done here instead of init() - Active::thread_init(SnortConfig::get_conf()); - - InitTag(); - EventTrace_Init(); - detection_filter_init(SnortConfig::get_conf()->detection_filter_config); - DetectionEngine::thread_init(); - - EventManager::open_outputs(); - IpsManager::setup_options(); - ActionManager::thread_init(SnortConfig::get_conf()); - FileService::thread_init(); - SideChannelManager::thread_init(); - HighAvailabilityManager::thread_init(); // must be before InspectorManager::thread_init(); - InspectorManager::thread_init(SnortConfig::get_conf()); - PacketTracer::thread_init(); - - // in case there are HA messages waiting, process them first - HighAvailabilityManager::process_receive(); - PacketManager::thread_init(); -} - -void Snort::thread_reinit(SnortConfig* sc) -{ - InspectorManager::thread_reinit(sc); - ActionManager::thread_reinit(sc); -} - -void Snort::thread_term() -{ - HighAvailabilityManager::thread_term_beginning(); - - if ( !SnortConfig::get_conf()->dirty_pig ) - Stream::purge_flows(); - - DetectionEngine::idle(); - InspectorManager::thread_stop(SnortConfig::get_conf()); - ModuleManager::accumulate(SnortConfig::get_conf()); - InspectorManager::thread_term(SnortConfig::get_conf()); - ActionManager::thread_term(SnortConfig::get_conf()); - - IpsManager::clear_options(); - EventManager::close_outputs(); - CodecManager::thread_term(); - HighAvailabilityManager::thread_term(); - SideChannelManager::thread_term(); - - s_packet = nullptr; - - SFDAQInstance* daq_instance = SFDAQ::get_local_instance(); - if ( daq_instance->was_started() ) - daq_instance->stop(); - SFDAQ::set_local_instance(nullptr); - delete daq_instance; - - PacketLatency::tterm(); - RuleLatency::tterm(); - - Profiler::consolidate_stats(); - - DetectionEngine::thread_term(); - detection_filter_term(); - EventTrace_Term(); - CleanupTag(); - FileService::thread_term(); - PacketTracer::thread_term(); - PacketManager::thread_term(); - - Active::thread_term(); - delete s_switcher; - delete[] s_data; -} - -bool Snort::inspect(Packet* p) -{ - // Need to include this b/c call is outside the detect tree - Profile detect_profile(detectPerfStats); - DeepProfile rebuilt_profile(rebuiltPacketPerfStats); - - DetectionEngine de; - return main_hook(p); -} - -DAQ_Verdict Snort::process_packet( - Packet* p, const DAQ_PktHdr_t* pkthdr, const uint8_t* pkt, bool is_frag) -{ - aux_counts.rx_bytes += pkthdr->caplen; - - PacketManager::decode(p, pkthdr, pkt, is_frag); - assert(p->pkth && p->pkt); - - PacketTracer::activate(*p); - - if (is_frag) - { - p->packet_flags |= (PKT_PSEUDO | PKT_REBUILT_FRAG); - p->pseudo_type = PSEUDO_PKT_IP; - } - - set_policy(p); // FIXIT-M should not need this here - - if ( !(p->packet_flags & PKT_IGNORE) ) - { - clear_file_data(); - main_hook(p); - - // FIXIT-L remove this onload when DAQng can push multiple packets - if ( p->flow ) - DetectionEngine::onload(p->flow); - } - - Active* act = p->active; - // process flow verdicts here - if ( act->packet_retry_requested() ) - { - return DAQ_VERDICT_RETRY; - } - else if ( act->session_was_blocked() ) - { - if ( !act->can_block() ) - return DAQ_VERDICT_PASS; - - if ( act->get_tunnel_bypass() ) - { - aux_counts.internal_blacklist++; - return DAQ_VERDICT_PASS; - } - - if ( SnortConfig::inline_mode() or act->packet_force_dropped() ) - return DAQ_VERDICT_BLACKLIST; - else - return DAQ_VERDICT_IGNORE; - } - - return DAQ_VERDICT_PASS; -} - -// process (wire-only) packet verdicts here -static DAQ_Verdict update_verdict(Packet* p, DAQ_Verdict verdict, int& inject) -{ - if ( p->active->packet_was_dropped() and p->active->can_block() ) - { - if ( verdict == DAQ_VERDICT_PASS ) - verdict = DAQ_VERDICT_BLOCK; - } - else if ( verdict == DAQ_VERDICT_RETRY ) - { - return verdict; - } - else if ( p->packet_flags & PKT_RESIZED ) - { - // we never increase, only trim, but daq doesn't support resizing wire packet - PacketManager::encode_update(p); - - if ( !SFDAQ::inject(p->pkth, 0, p->pkt, p->pkth->pktlen) ) - { - inject = 1; - verdict = DAQ_VERDICT_BLOCK; - } - } - else if ( p->packet_flags & PKT_MODIFIED ) - { - // this packet was normalized and/or has replacements - PacketManager::encode_update(p); - verdict = DAQ_VERDICT_REPLACE; - } - else if ( (p->packet_flags & PKT_IGNORE) || - (p->flow && p->flow->get_ignore_direction( ) == SSN_DIR_BOTH) ) - { - if ( !p->active->get_tunnel_bypass() ) - { - verdict = DAQ_VERDICT_WHITELIST; - } - else - { - verdict = DAQ_VERDICT_PASS; - aux_counts.internal_whitelist++; - } - } - else if ( p->ptrs.decode_flags & DECODE_PKT_TRUST ) - { - if (p->flow) - p->flow->set_ignore_direction(SSN_DIR_BOTH); - verdict = DAQ_VERDICT_WHITELIST; - } - else - { - verdict = DAQ_VERDICT_PASS; - } - return verdict; -} - -DAQ_Verdict Snort::packet_callback( - void*, const DAQ_PktHdr_t* pkthdr, const uint8_t* pkt) -{ - set_default_policy(); - Profile profile(totalPerfStats); - - pc.total_from_daq++; - packet_time_update(&pkthdr->ts); - - if ( SnortConfig::get_conf()->pkt_skip && pc.total_from_daq <= SnortConfig::get_conf()->pkt_skip ) - return DAQ_VERDICT_PASS; - - s_switcher->start(); - s_packet = s_switcher->get_context()->packet; - s_packet->context->packet_number = pc.total_from_daq; - - DetectionEngine::reset(); - - sfthreshold_reset(); - ActionManager::reset_queue(s_packet); - - DAQ_Verdict verdict = process_packet(s_packet, pkthdr, pkt); - ActionManager::execute(s_packet); - - int inject = 0; - verdict = update_verdict(s_packet, verdict, inject); - - if (PacketTracer::is_active()) - { - PacketTracer::log("Policies: Network %u, Inspection %u, Detection %u\n", - get_network_policy()->user_policy_id, get_inspection_policy()->user_policy_id, - get_ips_policy()->user_policy_id); - PacketTracer::log("Verdict: %s\n", SFDAQ::verdict_to_string(verdict)); - - PacketTracer::dump(pkthdr); - } - - HighAvailabilityManager::process_update(s_packet->flow, pkthdr); - - Stream::timeout_flows(pkthdr->ts.tv_sec); - HighAvailabilityManager::process_receive(); - - s_packet->pkth = nullptr; // no longer avail upon sig segv - - if ( SnortConfig::get_conf()->pkt_cnt && pc.total_from_daq >= SnortConfig::get_conf()->pkt_cnt ) - SFDAQ::break_loop(-1); - - // Check for resume(n) - else if ((s_pause.pause_cnt && pc.total_from_daq >= s_pause.pause_cnt) -#ifdef REG_TEST // pause-after-n - || ( SnortConfig::get_conf()->pkt_pause_cnt && !s_pause.was_paused && - pc.total_from_daq >= SnortConfig::get_conf()->pkt_pause_cnt ) -#endif - ) - { - SFDAQ::break_loop(0); - s_pause.was_paused = s_pause.pause = true; - } - - s_switcher->stop(); - - return verdict; -} - diff --git a/src/main/snort.h b/src/main/snort.h index ad467be24..0d51b1686 100644 --- a/src/main/snort.h +++ b/src/main/snort.h @@ -31,24 +31,10 @@ class ContextSwitcher; namespace snort { class Flow; +class SFDAQInstance; struct Packet; struct SnortConfig; -typedef bool (* MainHook_f)(Packet*); - -class TestPause -{ -public: - bool get_pause() { return pause; } - void clear_pause() { pause = false; } - void set_pause_cnt(int cnt); - -public: - bool pause = false; - bool was_paused = false; - uint64_t pause_cnt = 0; -}; - class Snort { public: @@ -64,29 +50,6 @@ public: static bool is_reloading(); static bool has_dropped_privileges(); - static bool thread_init_privileged(const char* intf); - static void thread_init_unprivileged(); - static void thread_reinit(SnortConfig*); - static void thread_term(); - - static void thread_idle(); - static void thread_rotate(); - - static void capture_packet(); - - static DAQ_Verdict process_packet( - Packet*, const DAQ_PktHdr_t*, const uint8_t* pkt, bool is_frag=false); - - static DAQ_Verdict packet_callback(void*, const DAQ_PktHdr_t*, const uint8_t*); - - static bool inspect(Packet*); - - static void set_main_hook(MainHook_f); - static ContextSwitcher* get_switcher(); - - SO_PUBLIC static Packet* get_packet(); - static TestPause& get_test_pause(); - private: static void init(int, char**); static void term(); diff --git a/src/main/snort_config.cc b/src/main/snort_config.cc index a0017c374..dc93daf5f 100644 --- a/src/main/snort_config.cc +++ b/src/main/snort_config.cc @@ -61,7 +61,7 @@ #include "utils/util.h" #include "utils/util_cstring.h" -#include "snort.h" +#include "analyzer.h" #include "thread_config.h" using namespace snort; @@ -614,7 +614,7 @@ bool SnortConfig::verify() return false; } - if (get_conf()->daq_config->mru_size != daq_config->mru_size) + if (get_conf()->daq_config->get_mru_size() != daq_config->get_mru_size()) { ReloadError("Snort Reload: Changing the packet snaplen " "configuration requires a restart.\n"); @@ -1021,14 +1021,14 @@ void SnortConfig::set_alert_mode(const char* val) output = val; output_flags |= OUTPUT_FLAG__ALERTS; - Snort::set_main_hook(DetectionEngine::inspect); + Analyzer::set_main_hook(DetectionEngine::inspect); } void SnortConfig::set_log_mode(const char* val) { if (strcasecmp(val, LOG_NONE) == 0) { - Snort::set_main_hook(snort_ignore); + Analyzer::set_main_hook(snort_ignore); EventManager::enable_logs(false); } else @@ -1036,7 +1036,7 @@ void SnortConfig::set_log_mode(const char* val) if ( !strcmp(val, LOG_DUMP) ) val = LOG_CODECS; output = val; - Snort::set_main_hook(snort_log); + Analyzer::set_main_hook(snort_log); } } diff --git a/src/main/snort_module.cc b/src/main/snort_module.cc index 95a04d16b..73eb83901 100644 --- a/src/main/snort_module.cc +++ b/src/main/snort_module.cc @@ -303,12 +303,18 @@ static const Parameter s_params[] = { "--daq", Parameter::PT_STRING, nullptr, nullptr, " select packet acquisition module (default is pcap)" }, + { "--daq-batch-size", Parameter::PT_INT, "1:", "64", + " set the DAQ receive batch size", }, + { "--daq-dir", Parameter::PT_STRING, nullptr, nullptr, " tell snort where to find desired DAQ" }, { "--daq-list", Parameter::PT_IMPLIED, nullptr, nullptr, "list packet acquisition modules available in optional dir, default is static modules only" }, + { "--daq-mode", Parameter::PT_ENUM, "passive | inline | read-file", nullptr, + " select DAQ module operating mode (overrides automatic selection)" }, + { "--daq-var", Parameter::PT_STRING, nullptr, nullptr, " specify extra DAQ configuration variable" }, @@ -606,13 +612,13 @@ public: { return GLOBAL; } private: - int instance_id; + SFDAQModuleConfig *module_config; }; bool SnortModule::begin(const char* fqn, int, SnortConfig*) { if (!strcmp(fqn, "snort")) - instance_id = -1; + module_config = nullptr; return true; } @@ -655,13 +661,7 @@ bool SnortModule::set(const char*, Value& v, SnortConfig* sc) sc->run_flags |= RUN_FLAG__STATIC_HASH; else if ( v.is("-i") ) - { - instance_id++; - if (instance_id > 0) - sc->daq_config->set_input_spec(v.get_string(), instance_id); - else - sc->daq_config->set_input_spec(v.get_string()); - } + sc->daq_config->add_input(v.get_string()); #ifdef SHELL else if ( v.is("-j") ) @@ -766,7 +766,10 @@ bool SnortModule::set(const char*, Value& v, SnortConfig* sc) sc->set_create_pid_file(true); else if ( v.is("--daq") ) - sc->daq_config->set_module_name(v.get_string()); + module_config = sc->daq_config->add_module_config(v.get_string()); + + else if ( v.is("--daq-batch-size") ) + sc->daq_config->set_batch_size(v.get_long()); else if ( v.is("--daq-dir") ) { @@ -776,15 +779,31 @@ bool SnortModule::set(const char*, Value& v, SnortConfig* sc) while ( getline(ss, path, ':') ) sc->daq_config->add_module_dir(path.c_str()); } + else if ( v.is("--daq-mode") ) + { + if (!module_config) + return false; + switch ( v.get_long() ) + { + case 0: + module_config->mode = SFDAQModuleConfig::SFDAQ_MODE_PASSIVE; + break; + case 1: + module_config->mode = SFDAQModuleConfig::SFDAQ_MODE_INLINE; + break; + case 2: + module_config->mode = SFDAQModuleConfig::SFDAQ_MODE_READ_FILE; + break; + } + } else if ( v.is("--daq-list") ) list_daqs(sc); else if ( v.is("--daq-var") ) { - if (instance_id < 0) - sc->daq_config->set_variable(v.get_string()); - else - sc->daq_config->set_variable(v.get_string(), instance_id); + if (!module_config) + return false; + module_config->set_variable(v.get_string()); } else if ( v.is("--dirty-pig") ) sc->set_dirty_pig(true); diff --git a/src/main/swapper.cc b/src/main/swapper.cc index f294c208f..f4ca31e16 100644 --- a/src/main/swapper.cc +++ b/src/main/swapper.cc @@ -25,6 +25,7 @@ #include "target_based/sftarget_reader.h" +#include "analyzer.h" #include "snort.h" #include "snort_config.h" @@ -77,7 +78,7 @@ Swapper::~Swapper() SFAT_Free(old_attribs); } -void Swapper::apply() +void Swapper::apply(Analyzer& analyzer) { if ( new_conf ) { @@ -85,7 +86,7 @@ void Swapper::apply() snort::SnortConfig::set_conf(new_conf); // FIXIT-M Determine whether we really want to do this before or after the set_conf if (reload) - snort::Snort::thread_reinit(new_conf); + analyzer.reinit(new_conf); } if ( new_attribs ) diff --git a/src/main/swapper.h b/src/main/swapper.h index 91dd6e52f..46a9aeeb8 100644 --- a/src/main/swapper.h +++ b/src/main/swapper.h @@ -27,6 +27,7 @@ namespace snort struct SnortConfig; } +class Analyzer; struct tTargetBasedConfig; class Swapper @@ -38,7 +39,7 @@ public: Swapper(tTargetBasedConfig*, tTargetBasedConfig*); ~Swapper(); - void apply(); + void apply(Analyzer&); static bool get_reload_in_progress() { return reload_in_progress; } static void set_reload_in_progress(bool rip) { reload_in_progress = rip; } diff --git a/src/managers/action_manager.cc b/src/managers/action_manager.cc index aa91ce7d6..42003fa9e 100644 --- a/src/managers/action_manager.cc +++ b/src/managers/action_manager.cc @@ -214,14 +214,17 @@ void ActionManager::thread_reinit(SnortConfig* sc) void ActionManager::thread_term(SnortConfig*) { - // Call tterm for every IPS action plugin ever configured during the lifetime of this thread - for ( auto& p : *s_tl_actors ) + if (s_tl_actors) { - if ( p.api->tterm ) - p.api->tterm(); + // Call tterm for every IPS action plugin ever configured during the lifetime of this thread + for ( auto& p : *s_tl_actors ) + { + if ( p.api->tterm ) + p.api->tterm(); + } + delete s_tl_actors; + s_tl_actors = nullptr; } - delete s_tl_actors; - s_tl_actors = nullptr; } void ActionManager::execute(Packet* p) diff --git a/src/network_inspectors/packet_capture/packet_capture.cc b/src/network_inspectors/packet_capture/packet_capture.cc index 68248a54d..bf3216f38 100644 --- a/src/network_inspectors/packet_capture/packet_capture.cc +++ b/src/network_inspectors/packet_capture/packet_capture.cc @@ -205,7 +205,7 @@ void PacketCapture::eval(Packet* p) return; if ( !bpf.bf_insns || bpf_filter(bpf.bf_insns, p->pkt, - p->pkth->caplen, p->pkth->pktlen) ) + p->pktlen, p->pkth->pktlen) ) { write_packet(p); cap_count_stats.matched++; @@ -221,7 +221,7 @@ void PacketCapture::write_packet(Packet* p) { struct pcap_pkthdr pcaphdr; pcaphdr.ts = p->pkth->ts; - pcaphdr.caplen = p->pkth->caplen; + pcaphdr.caplen = p->pktlen; pcaphdr.len = p->pkth->pktlen; pcap_dump((unsigned char*)dumper, &pcaphdr, p->pkt); pcap_dump_flush(dumper); @@ -291,9 +291,9 @@ static Packet* init_null_packet() static Packet p(false); static DAQ_PktHdr_t h; - p.pkt = nullptr; p.pkth = &h; - h.caplen = 0; + p.pkt = nullptr; + p.pktlen = 0; h.pktlen = 0; return &p; @@ -394,10 +394,10 @@ TEST_CASE("blank filter", "[PacketCapture]") Packet p(false); DAQ_PktHdr_t daq_hdr; - p.pkt = cooked; p.pkth = &daq_hdr; + p.pkt = cooked; + p.pktlen = sizeof(cooked); - daq_hdr.caplen = sizeof(cooked); daq_hdr.pktlen = sizeof(cooked); CaptureModule mod; @@ -479,7 +479,9 @@ TEST_CASE("bpf filter", "[PacketCapture]") p_match.pkt = match; p_non_match.pkt = non_match; - daq_hdr.caplen = sizeof(match); + p_match.pktlen = sizeof(match); + p_non_match.pktlen = sizeof(match); + daq_hdr.pktlen = sizeof(match); CaptureModule mod; diff --git a/src/network_inspectors/packet_tracer/packet_tracer.cc b/src/network_inspectors/packet_tracer/packet_tracer.cc index 493459839..222fc00af 100644 --- a/src/network_inspectors/packet_tracer/packet_tracer.cc +++ b/src/network_inspectors/packet_tracer/packet_tracer.cc @@ -31,7 +31,7 @@ #include "detection/ips_context.h" #include "log/log.h" #include "log/messages.h" -#include "packet_io/sfdaq.h" +#include "packet_io/sfdaq_instance.h" #include "protocols/eth.h" #include "protocols/icmp4.h" #include "protocols/ip.h" @@ -112,13 +112,13 @@ void PacketTracer::dump(char* output_buff, unsigned int len) s_pkt_trace->reset(); } -void PacketTracer::dump(const DAQ_PktHdr_t* pkt_hdr) +void PacketTracer::dump(Packet* p) { if (is_paused()) return; if (s_pkt_trace->daq_activated) - s_pkt_trace->dump_to_daq(pkt_hdr); + s_pkt_trace->dump_to_daq(p); if (s_pkt_trace->user_enabled or s_pkt_trace->shell_enabled) LogMessage(s_pkt_trace->log_fh, "%s\n", s_pkt_trace->buffer); @@ -341,12 +341,12 @@ void PacketTracer::add_packet_type_info(const Packet& p) PacketTracer::log("Packet %" PRIu64 ": TCP %s, %s, seq %u, ack %u, dsize %u%s\n", p.context->packet_number, tcpFlags, timestamp, p.ptrs.tcph->seq(), p.ptrs.tcph->ack(), p.dsize, - (p.pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET) ? ", retry pkt" : ""); + p.is_retry() ? ", retry pkt" : ""); else PacketTracer::log("Packet %" PRIu64 ": TCP %s, %s, seq %u, dsize %u%s\n", p.context->packet_number, tcpFlags, timestamp, p.ptrs.tcph->seq(), p.dsize, - (p.pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET) ? ", retry pkt" : ""); + p.is_retry() ? ", retry pkt" : ""); break; } @@ -416,9 +416,10 @@ void PacketTracer::open_file() } } -void PacketTracer::dump_to_daq(const DAQ_PktHdr_t* pkt_hdr) +void PacketTracer::dump_to_daq(Packet* p) { - SFDAQ::get_local_instance()->modify_flow_pkt_trace(pkt_hdr, reason, + assert(p); + p->daq_instance->modify_flow_pkt_trace(p->daq_msg, reason, (uint8_t *)buffer, buff_len + 1); } @@ -475,7 +476,7 @@ public: static uint8_t get_dump_reason() { return ((TestPacketTracer*)s_pkt_trace)->dump_reason; } - void dump_to_daq(const DAQ_PktHdr_t*) override + void dump_to_daq(Packet*) override { dump_reason = reason; } static std::vector get_mutes() diff --git a/src/network_inspectors/packet_tracer/packet_tracer.h b/src/network_inspectors/packet_tracer/packet_tracer.h index 38a30470f..df8bdbdf9 100644 --- a/src/network_inspectors/packet_tracer/packet_tracer.h +++ b/src/network_inspectors/packet_tracer/packet_tracer.h @@ -24,7 +24,6 @@ #include #include #include -#include #include #include "main/snort_types.h" @@ -102,7 +101,7 @@ public: static void thread_term(); static void dump(char* output_buff, unsigned int len); - static void dump(const DAQ_PktHdr_t*); + static void dump(Packet*); static void configure(bool status, const std::string& file_name); static void set_constraints(const PTSessionConstraints* constraints); @@ -151,7 +150,7 @@ protected: const char *get_debug_session() { return debug_session; } virtual void open_file(); - virtual void dump_to_daq(const DAQ_PktHdr_t*); + virtual void dump_to_daq(Packet*); virtual void reset(); }; diff --git a/src/network_inspectors/perf_monitor/flow_ip_tracker.cc b/src/network_inspectors/perf_monitor/flow_ip_tracker.cc index 261d66a90..a35e09ab8 100644 --- a/src/network_inspectors/perf_monitor/flow_ip_tracker.cc +++ b/src/network_inspectors/perf_monitor/flow_ip_tracker.cc @@ -137,7 +137,7 @@ void FlowIPTracker::update(Packet* p) const SfIp* src_addr = p->ptrs.ip_api.get_src(); const SfIp* dst_addr = p->ptrs.ip_api.get_dst(); - int len = p->pkth->caplen; + int len = p->pktlen; if (p->ptrs.tcph) type = SFS_TYPE_TCP; diff --git a/src/network_inspectors/perf_monitor/flow_tracker.cc b/src/network_inspectors/perf_monitor/flow_tracker.cc index 1ffc65a60..8249b7f99 100644 --- a/src/network_inspectors/perf_monitor/flow_tracker.cc +++ b/src/network_inspectors/perf_monitor/flow_tracker.cc @@ -74,7 +74,7 @@ void FlowTracker::update(Packet* p) { if (!p->is_rebuilt()) { - auto len = p->pkth->caplen; + auto len = p->pktlen; if (p->ptrs.tcph) update_transport_flows(p->ptrs.sp, p->ptrs.dp, tcp, len); @@ -163,7 +163,7 @@ public: TEST_CASE("no protocol", "[FlowTracker]") { Packet p; - uint32_t* len_ptr = &const_cast(p.pkth)->caplen; + uint32_t* len_ptr = &p.pktlen; PerfConfig config; config.format = PerfFormat::MOCK; @@ -208,7 +208,7 @@ TEST_CASE("icmp", "[FlowTracker]") { Packet p; icmp::ICMPHdr icmp; - uint32_t* len_ptr = &const_cast(p.pkth)->caplen; + uint32_t* len_ptr = &p.pktlen; uint8_t* type_ptr = (uint8_t*) &icmp.type; PerfConfig config; @@ -253,7 +253,7 @@ TEST_CASE("tcp", "[FlowTracker]") { Packet p; tcp::TCPHdr tcp; - uint32_t* len_ptr = &const_cast(p.pkth)->caplen; + uint32_t* len_ptr = &p.pktlen; PerfConfig config; config.format = PerfFormat::MOCK; @@ -307,7 +307,7 @@ TEST_CASE("udp", "[FlowTracker]") { Packet p; udp::UDPHdr udp; - uint32_t* len_ptr = &const_cast(p.pkth)->caplen; + uint32_t* len_ptr = &p.pktlen; PerfConfig config; config.format = PerfFormat::MOCK; diff --git a/src/packet_io/CMakeLists.txt b/src/packet_io/CMakeLists.txt index 563bfd465..4c2807d9b 100644 --- a/src/packet_io/CMakeLists.txt +++ b/src/packet_io/CMakeLists.txt @@ -2,6 +2,7 @@ set ( PACKET_IO_INCLUDES active.h sfdaq.h + sfdaq_instance.h ) if (ENABLE_UNIT_TESTS) @@ -10,6 +11,20 @@ if (ENABLE_UNIT_TESTS) ) endif (ENABLE_UNIT_TESTS) +if (ENABLE_STATIC_DAQ) + foreach (STATIC_MODULE_NAME IN LISTS DAQ_STATIC_MODULES) + string(APPEND DAQ_STATIC_MODULE_EXTERNS "extern const DAQ_ModuleAPI_t ${STATIC_MODULE_NAME}_daq_module_data;\n") + string(APPEND DAQ_STATIC_MODULE_DATA_ARRAY " &${STATIC_MODULE_NAME}_daq_module_data,\n") + endforeach () + configure_file ( + "${CMAKE_CURRENT_SOURCE_DIR}/sfdaq_static_modules.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/sfdaq_static_modules.h" + ) + set_source_files_properties(sfdaq.cc PROPERTIES COMPILE_DEFINITIONS ENABLE_STATIC_DAQ) + include_directories(${CMAKE_CURRENT_BINARY_DIR}) + set(STATIC_DAQ_MODULES_HEADER "sfdaq_static_modules.h") +endif () + add_library (packet_io OBJECT active.cc active.h @@ -17,10 +32,13 @@ add_library (packet_io OBJECT sfdaq.h sfdaq_config.cc sfdaq_config.h + sfdaq_instance.cc + sfdaq_instance.h sfdaq_module.cc sfdaq_module.h trough.cc trough.h + ${STATIC_DAQ_MODULES_HEADER} ${TEST_FILES} ) diff --git a/src/packet_io/active.cc b/src/packet_io/active.cc index acd7ba59c..1270f7265 100644 --- a/src/packet_io/active.cc +++ b/src/packet_io/active.cc @@ -53,7 +53,7 @@ THREAD_LOCAL bool Active::s_suspend = false; THREAD_LOCAL Active::Counts snort::active_counts; typedef int (* send_t) ( - const DAQ_PktHdr_t* h, int rev, const uint8_t* buf, uint32_t len); + DAQ_Msg_h msg, int rev, const uint8_t* buf, uint32_t len); static THREAD_LOCAL eth_t* s_link = nullptr; static THREAD_LOCAL ip_t* s_ipnet = nullptr; @@ -63,7 +63,7 @@ static THREAD_LOCAL send_t s_send = SFDAQ::inject; // helpers int Active::send_eth( - const DAQ_PktHdr_t*, int, const uint8_t* buf, uint32_t len) + DAQ_Msg_h, int, const uint8_t* buf, uint32_t len) { ssize_t sent = eth_send(s_link, buf, len); active_counts.injects++; @@ -71,7 +71,7 @@ int Active::send_eth( } int Active::send_ip( - const DAQ_PktHdr_t*, int, const uint8_t* buf, uint32_t len) + DAQ_Msg_h, int, const uint8_t* buf, uint32_t len) { ssize_t sent = ip_send(s_ipnet, buf, len); active_counts.injects++; @@ -209,7 +209,7 @@ void Active::send_reset(Packet* p, EncodeFlags ef) if ( !rej ) return; - s_send(p->pkth, !(ef & ENC_FLAG_FWD), rej, len); + s_send(p->daq_msg, !(ef & ENC_FLAG_FWD), rej, len); } } @@ -226,7 +226,7 @@ void Active::send_unreach(Packet* p, snort::UnreachResponse type) if ( !rej ) return; - s_send(p->pkth, 1, rej, len); + s_send(p->daq_msg, 1, rej, len); } bool Active::send_data( @@ -246,7 +246,7 @@ bool Active::send_data( if ( seg ) { - s_send(p->pkth, !(tmp_flags & ENC_FLAG_FWD), seg, plen); + s_send(p->daq_msg, !(tmp_flags & ENC_FLAG_FWD), seg, plen); active_counts.injects++; } } @@ -268,7 +268,7 @@ bool Active::send_data( if ( !seg ) return false; - s_send(p->pkth, !(flags & ENC_FLAG_FWD), seg, plen); + s_send(p->daq_msg, !(flags & ENC_FLAG_FWD), seg, plen); active_counts.injects++; buf += toSend; @@ -284,7 +284,7 @@ bool Active::send_data( if ( !seg ) return false; - s_send(p->pkth, !(flags & ENC_FLAG_FWD), seg, plen); + s_send(p->daq_msg, !(flags & ENC_FLAG_FWD), seg, plen); active_counts.injects++; if (flags & ENC_FLAG_RST_CLNT) @@ -296,7 +296,7 @@ bool Active::send_data( if ( seg ) { - s_send(p->pkth, !(flags & ENC_FLAG_FWD), seg, plen); + s_send(p->daq_msg, !(flags & ENC_FLAG_FWD), seg, plen); active_counts.injects++; } } @@ -320,7 +320,7 @@ void Active::inject_data( if ( !seg ) return; - s_send(p->pkth, !(flags & ENC_FLAG_FWD), seg, plen); + s_send(p->daq_msg, !(flags & ENC_FLAG_FWD), seg, plen); } //-------------------------------------------------------------------- @@ -422,11 +422,11 @@ bool Active::daq_retry_packet(const Packet* p) { bool retry_queued = false; - if ( ( active_action == ACT_PASS ) and SFDAQ::can_retry() ) + if ( !p->is_rebuilt() && (active_action == ACT_PASS) ) { if ( SFDAQ::forwarding_packet(p->pkth) ) { - if(p->packet_flags & PKT_RETRANSMIT) + if (p->packet_flags & PKT_RETRANSMIT) active_action = ACT_DROP; // Don't add retransmits to retry queue. else active_action = ACT_RETRY; diff --git a/src/packet_io/active.h b/src/packet_io/active.h index d3c116498..f20842ae1 100644 --- a/src/packet_io/active.h +++ b/src/packet_io/active.h @@ -129,8 +129,8 @@ public: private: static bool open(const char*); static void close(); - static int send_eth(const DAQ_PktHdr_t*, int, const uint8_t* buf, uint32_t len); - static int send_ip(const DAQ_PktHdr_t*, int, const uint8_t* buf, uint32_t len); + static int send_eth(DAQ_Msg_h, int, const uint8_t* buf, uint32_t len); + static int send_ip(DAQ_Msg_h, int, const uint8_t* buf, uint32_t len); void update_status(const Packet*, bool force = false); void daq_update_status(const Packet*); diff --git a/src/packet_io/sfdaq.cc b/src/packet_io/sfdaq.cc index 4030e1316..c99cf2915 100644 --- a/src/packet_io/sfdaq.cc +++ b/src/packet_io/sfdaq.cc @@ -25,61 +25,50 @@ #include "sfdaq.h" -extern "C" { #include -#include -} - -#include -#include #include "log/messages.h" #include "main/snort_config.h" -#include "protocols/packet.h" -#include "protocols/vlan.h" #include "sfdaq_config.h" +#include "sfdaq_instance.h" +#ifdef ENABLE_STATIC_DAQ +#include "sfdaq_static_modules.h" +#endif using namespace snort; using namespace std; #ifdef DEFAULT_DAQ -#define XSTR(s) STR(s) -#define STR(s) #s -#define DAQ_DEFAULT XSTR(DEFAULT_DAQ) +#define DAQ_DEFAULT STRINGIFY_MX(DEFAULT_DAQ) #else #define DAQ_DEFAULT "pcap" #endif -static const int DEFAULT_PKT_SNAPLEN = 1518; - // common for all daq threads / instances -static const DAQ_Module_t* daq_mod = nullptr; -static DAQ_Mode daq_mode = DAQ_MODE_PASSIVE; -static uint32_t snap = DEFAULT_PKT_SNAPLEN; +static DAQ_Config_h daqcfg = nullptr; +static DAQ_Mode default_daq_mode = DAQ_MODE_PASSIVE; +static string daq_module_names; static bool loaded = false; -static std::mutex bpf_gate; // specific for each thread / instance static THREAD_LOCAL SFDAQInstance *local_instance = nullptr; -/* - * SFDAQ - */ - -void SFDAQ::load(const SnortConfig* sc) +void SFDAQ::load(const SFDAQConfig* cfg) { - const char** dirs = new const char*[sc->daq_config->module_dirs.size() + 1]; + const char** dirs = new const char*[cfg->module_dirs.size() + 1]; int i = 0; - for (string& module_dir : sc->daq_config->module_dirs) + for (const string& module_dir : cfg->module_dirs) dirs[i++] = module_dir.c_str(); dirs[i] = nullptr; - int err = daq_load_modules(dirs); - +#ifdef ENABLE_STATIC_DAQ + daq_load_static_modules(static_daq_modules); +#endif + int err = daq_load_dynamic_modules(dirs); if (err) - FatalError("Can't load DAQ modules = %d\n", err); + FatalError("Could not load dynamic DAQ modules! (%d)\n", err); delete[] dirs; @@ -94,41 +83,63 @@ void SFDAQ::unload() void SFDAQ::print_types(ostream& ostr) { - DAQ_Module_Info_t* list = nullptr; - int i, nMods = daq_get_module_list(&list); + DAQ_Module_h module = daq_modules_first(); - if (nMods) + if (module) ostr << "Available DAQ modules:" << endl; else ostr << "No available DAQ modules (try adding directories with --daq-dir)." << endl; - for (i = 0; i < nMods; i++) + while (module) { - ostr << list[i].name << "(v" << list[i].version << "):"; + ostr << daq_module_get_name(module) << "(v" << daq_module_get_version(module) << "):"; - if (list[i].type & DAQ_TYPE_FILE_CAPABLE) + uint32_t type = daq_module_get_type(module); + + if (type & DAQ_TYPE_FILE_CAPABLE) ostr << " readback"; - if (list[i].type & DAQ_TYPE_INTF_CAPABLE) + if (type & DAQ_TYPE_INTF_CAPABLE) ostr << " live"; - if (list[i].type & DAQ_TYPE_INLINE_CAPABLE) + if (type & DAQ_TYPE_INLINE_CAPABLE) ostr << " inline"; - if (list[i].type & DAQ_TYPE_MULTI_INSTANCE) + if (type & DAQ_TYPE_MULTI_INSTANCE) ostr << " multi"; - if (!(list[i].type & DAQ_TYPE_NO_UNPRIV)) + if (!(type & DAQ_TYPE_NO_UNPRIV)) ostr << " unpriv"; + if (type & DAQ_TYPE_WRAPPER) + ostr << " wrapper"; + ostr << endl; + + const DAQ_VariableDesc_t *var_desc_table; + int num_var_descs = daq_module_get_variable_descs(module, &var_desc_table); + if (num_var_descs > 0) + { + ostr << " Variables:" << endl; + for (int i = 0; i < num_var_descs; i++) + { + ostr << " " << var_desc_table[i].name << " "; + if (var_desc_table[i].flags & DAQ_VAR_DESC_REQUIRES_ARGUMENT) + ostr << " "; + else if (!(var_desc_table[i].flags & DAQ_VAR_DESC_FORBIDS_ARGUMENT)) + ostr << "[arg] "; + ostr << "- " << var_desc_table[i].description << endl; + } + } + + module = daq_modules_next(); } - daq_free_module_list(list, nMods); } -static int DAQ_ValidateModule(DAQ_Mode mode) +/* +static int DAQ_ValidateModule(DAQ_Module_h module, DAQ_Mode mode) { - uint32_t have = daq_get_type(daq_mod); + uint32_t have = daq_module_get_type(module); uint32_t need = 0; if (mode == DAQ_MODE_READ_FILE) @@ -142,42 +153,153 @@ static int DAQ_ValidateModule(DAQ_Mode mode) return ((have & need) != 0); } +*/ -void SFDAQ::init(const SnortConfig* sc) +static bool AddDaqModuleConfig(const SFDAQModuleConfig *dmc) { - if (!loaded) - load(sc); + const char* module_name = dmc->name.c_str(); + DAQ_Module_h module = daq_find_module(module_name); + if (!module) + { + ErrorMessage("Could not find requested DAQ module: %s\n", module_name); + return false; + } - const char* type = DAQ_DEFAULT; + DAQ_ModuleConfig_h modcfg; + int rval; + if ((rval = daq_module_config_new(&modcfg, module)) != DAQ_SUCCESS) + { + ErrorMessage("Error allocating a new DAQ module configuration object! (%d)\n", rval); + return false; + } - if (!sc->daq_config->module_name.empty()) - type = sc->daq_config->module_name.c_str(); + DAQ_Mode mode; + if (dmc->mode == SFDAQModuleConfig::SFDAQ_MODE_PASSIVE) + mode = DAQ_MODE_PASSIVE; + else if (dmc->mode == SFDAQModuleConfig::SFDAQ_MODE_INLINE) + mode = DAQ_MODE_INLINE; + else if (dmc->mode == SFDAQModuleConfig::SFDAQ_MODE_READ_FILE) + mode = DAQ_MODE_READ_FILE; + else + mode = default_daq_mode; + daq_module_config_set_mode(modcfg, mode); - daq_mod = daq_find_module(type); + for (auto& kvp : dmc->variables) + { + const char* key = kvp.first.c_str(); + const char* value = kvp.second.length() ? kvp.second.c_str() : nullptr; + if (daq_module_config_set_variable(modcfg, key, value) != DAQ_SUCCESS) + { + ErrorMessage("Error setting DAQ configuration variable with key '%s' and value '%s'! (%d)", + key, value, rval); + daq_module_config_destroy(modcfg); + return false; + } + } - if (!daq_mod) - FatalError("Can't find %s DAQ\n", type); + if ((rval = daq_config_push_module_config(daqcfg, modcfg)) != DAQ_SUCCESS) + { + ErrorMessage("Error pushing DAQ module configuration for '%s' onto the DAQ config! (%d)\n", + daq_module_get_name(module), rval); + daq_module_config_destroy(modcfg); + return false; + } - snap = (sc->daq_config->mru_size > 0) ? sc->daq_config->mru_size : DEFAULT_PKT_SNAPLEN; + if (!daq_module_names.empty()) + daq_module_names.insert(0, 1, ':'); + daq_module_names.insert(0, module_name); + + return true; +} + +bool SFDAQ::init(const SFDAQConfig* cfg) +{ + if (!loaded) + load(cfg); + + int rval; if (SnortConfig::adaptor_inline_mode()) - daq_mode = DAQ_MODE_INLINE; + default_daq_mode = DAQ_MODE_INLINE; else if (SnortConfig::read_mode()) - daq_mode = DAQ_MODE_READ_FILE; + default_daq_mode = DAQ_MODE_READ_FILE; else - daq_mode = DAQ_MODE_PASSIVE; + default_daq_mode = DAQ_MODE_PASSIVE; + + if ((rval = daq_config_new(&daqcfg)) != DAQ_SUCCESS) + { + ErrorMessage("Error allocating a new DAQ configuration object! (%d)\n", rval); + return false; + } + daq_config_set_msg_pool_size(daqcfg, cfg->get_batch_size() * 4); + daq_config_set_snaplen(daqcfg, cfg->get_mru_size()); + daq_config_set_timeout(daqcfg, cfg->timeout); + + /* If no modules were specified, try to automatically configure with the default. */ + if (cfg->module_configs.empty()) + { + SFDAQModuleConfig dmc; + dmc.name = DAQ_DEFAULT; + if (!AddDaqModuleConfig(&dmc)) + { + daq_config_destroy(daqcfg); + daqcfg = nullptr; + return false; + } + } + /* Otherwise, if the module stack doesn't have a terminal module at the bottom, default + to a hardcoded base of the PCAP DAQ module in read-file mode. This is a convenience + provided to emulate the previous dump/regtest DAQ module behavior. */ + else + { + const char* module_name = cfg->module_configs[0]->name.c_str(); + DAQ_Module_h module = daq_find_module(module_name); + if (module && (daq_module_get_type(module) & DAQ_TYPE_WRAPPER)) + { + SFDAQModuleConfig dmc; + dmc.name = "pcap"; + dmc.mode = SFDAQModuleConfig::SFDAQ_MODE_READ_FILE; + if (!AddDaqModuleConfig(&dmc)) + { + daq_config_destroy(daqcfg); + daqcfg = nullptr; + return false; + } + } + } + + for (SFDAQModuleConfig* dmc : cfg->module_configs) + { + if (!AddDaqModuleConfig(dmc)) + { + daq_config_destroy(daqcfg); + daqcfg = nullptr; + return false; + } + } + +/* if (!DAQ_ValidateModule(daq_mode)) FatalError("%s DAQ does not support %s.\n", type, daq_mode_string(daq_mode)); - LogMessage("%s DAQ configured to %s.\n", type, daq_mode_string(daq_mode)); +*/ + LogMessage("%s DAQ configured to %s.\n", daq_module_names.c_str(), daq_mode_string(default_daq_mode)); + + return true; } void SFDAQ::term() { + if (daqcfg) + { + daq_config_destroy(daqcfg); + daqcfg = nullptr; + } +#ifndef REG_TEST if (loaded) unload(); - daq_mod = nullptr; +#endif } const char* SFDAQ::verdict_to_string(DAQ_Verdict verdict) @@ -188,25 +310,26 @@ const char* SFDAQ::verdict_to_string(DAQ_Verdict verdict) bool SFDAQ::forwarding_packet(const DAQ_PktHdr_t* h) { // DAQ mode is inline and the packet will be forwarded? - return (daq_mode == DAQ_MODE_INLINE && !(h->flags & DAQ_PKT_FLAG_NOT_FORWARDING)); -} - -const char* SFDAQ::get_type() -{ - return daq_mod ? daq_get_name(daq_mod) : "error"; + return (default_daq_mode == DAQ_MODE_INLINE && !(h->flags & DAQ_PKT_FLAG_NOT_FORWARDING)); } -// Snort has its own snap applied to packets it acquires via the DAQ. This -// should not be confused with the snap that was used to capture a pcap which -// may be different. -uint32_t SFDAQ::get_snap_len() +bool SFDAQ::can_run_unprivileged() { - return snap; + // Iterate over the configured modules to see if any of them don't support unprivileged operation + DAQ_ModuleConfig_h modcfg = daq_config_top_module_config(daqcfg); + while (modcfg) + { + DAQ_Module_h module = daq_module_config_get_module(modcfg); + if (daq_module_get_type(module) & DAQ_TYPE_NO_UNPRIV) + return false; + modcfg = daq_config_next_module_config(daqcfg); + } + return true; } -bool SFDAQ::unprivileged() +bool SFDAQ::init_instance(SFDAQInstance* instance, const string& bpf_string) { - return !(daq_get_type(daq_mod) & DAQ_TYPE_NO_UNPRIV); + return instance->init(daqcfg, bpf_string); } /* @@ -223,10 +346,9 @@ SFDAQInstance* SFDAQ::get_local_instance() return local_instance; } -const char* SFDAQ::get_interface_spec() +const char* SFDAQ::get_input_spec() { - assert(local_instance->get_interface_spec()); - return local_instance->get_interface_spec(); + return local_instance->get_input_spec(); } int SFDAQ::get_base_protocol() @@ -249,24 +371,14 @@ bool SFDAQ::can_replace() return local_instance && local_instance->can_replace(); } -bool SFDAQ::can_retry() -{ - return local_instance && local_instance->can_retry(); -} - bool SFDAQ::get_tunnel_bypass(uint8_t proto) { return local_instance && local_instance->get_tunnel_bypass(proto); } -int SFDAQ::inject(const DAQ_PktHdr_t* hdr, int rev, const uint8_t* buf, uint32_t len) +int SFDAQ::inject(DAQ_Msg_h msg, int rev, const uint8_t* buf, uint32_t len) { - return local_instance->inject(hdr, rev, buf, len); -} - -bool SFDAQ::break_loop(int error) -{ - return local_instance->break_loop(error); + return local_instance->inject(msg, rev, buf, len); } const DAQ_Stats_t* SFDAQ::get_stats() @@ -274,16 +386,15 @@ const DAQ_Stats_t* SFDAQ::get_stats() return local_instance->get_stats(); } -const char* SFDAQ::get_input_spec(const SnortConfig* sc, unsigned instance_id) +const char* SFDAQ::get_input_spec(const SFDAQConfig* cfg, unsigned instance_id) { - auto it = sc->daq_config->instances.find(instance_id); - if (it != sc->daq_config->instances.end() && !it->second->input_spec.empty()) - return it->second->input_spec.c_str(); + if (cfg->inputs.empty()) + return nullptr; - if (!sc->daq_config->input_spec.empty()) - return sc->daq_config->input_spec.c_str(); + if (instance_id > 0 && instance_id < cfg->inputs.size()) + return cfg->inputs[instance_id].c_str(); - return nullptr; + return cfg->inputs[0].c_str(); } const char* SFDAQ::default_type() @@ -291,413 +402,3 @@ const char* SFDAQ::default_type() return DAQ_DEFAULT; } -/* - * SFDAQInstance - */ - -SFDAQInstance::SFDAQInstance(const char* intf) -{ - if (intf) - interface_spec = intf; - daq_hand = nullptr; - daq_dlt = -1; - s_error = DAQ_SUCCESS; - memset(&daq_stats, 0, sizeof(daq_stats)); - daq_tunnel_mask = 0; -} - -SFDAQInstance::~SFDAQInstance() -{ - if (daq_hand) - daq_shutdown(daq_mod, daq_hand); -} - -static bool DAQ_ValidateInstance(void* daq_hand) -{ - uint32_t caps = daq_get_capabilities(daq_mod, daq_hand); - - if (!SnortConfig::adaptor_inline_mode()) - return true; - - if (!(caps & DAQ_CAPA_BLOCK)) - ParseWarning(WARN_DAQ, "inline mode configured but DAQ can't block packets.\n"); - - return true; -} - -bool SFDAQInstance::configure(const SnortConfig* sc) -{ - DAQ_Config_t cfg; - const char* type = daq_get_name(daq_mod); - char buf[256] = ""; - int err; - - memset(&cfg, 0, sizeof(cfg)); - - cfg.name = const_cast(interface_spec.c_str()); - cfg.snaplen = snap; - cfg.timeout = sc->daq_config->timeout; - cfg.mode = daq_mode; - cfg.extra = nullptr; - cfg.flags = 0; - - for (auto& kvp : sc->daq_config->variables) - { - daq_config_set_value(&cfg, kvp.first.c_str(), - kvp.second.length() ? kvp.second.c_str() : nullptr); - } - - auto it = sc->daq_config->instances.find(get_instance_id()); - if (it != sc->daq_config->instances.end()) - { - for (auto& kvp : it->second->variables) - { - daq_config_set_value(&cfg, kvp.first.c_str(), - kvp.second.length() ? kvp.second.c_str() : nullptr); - } - } - - if (!SnortConfig::read_mode()) - { - if (!(sc->run_flags & RUN_FLAG__NO_PROMISCUOUS)) - cfg.flags |= DAQ_CFG_PROMISC; - } - - // FIXIT-M - This is sort of an abomination and would ideally be configurable ... - if (!strcasecmp(type, "dump") or !strcasecmp(type, "regtest")) - cfg.extra = reinterpret_cast(const_cast(daq_find_module("pcap"))); - - err = daq_initialize(daq_mod, &cfg, &daq_hand, buf, sizeof(buf)); - if (err) - { - ErrorMessage("Can't initialize DAQ %s (%d) - %s\n", type, err, buf); - return false; - } - daq_config_clear_values(&cfg); - - if (!DAQ_ValidateInstance(daq_hand)) - FatalError("DAQ configuration incompatible with intended operation.\n"); - - set_filter(sc->bpf_filter.c_str()); - - return true; -} - -void SFDAQInstance::reload() -{ - void* old_config = nullptr; - void* new_config = nullptr; - if (daq_mod && daq_hand) - { - if ( ( daq_hup_prep(daq_mod, daq_hand, &new_config) == DAQ_SUCCESS ) and - ( daq_hup_apply(daq_mod, daq_hand, new_config, &old_config) == DAQ_SUCCESS ) ) - { - daq_hup_post(daq_mod, daq_hand, old_config); - } - } -} - -void SFDAQInstance::abort() -{ - if (was_started()) - stop(); - - //DAQ_Delete(); - //DAQ_Term(); FIXIT-L this must be called from main thread on abort -} - -const char* SFDAQInstance::get_interface_spec() -{ - return interface_spec.c_str(); -} - -// That distinction does not hold with datalink types. Snort must use whatever -// datalink type the DAQ coughs up as its base protocol decoder. For pcaps, -// the datalink type in the file must be used - which may not be known until -// start. The value is cached here since it used for packet operations like -// logging and is needed at shutdown. This avoids sequencing issues. -int SFDAQInstance::get_base_protocol() -{ - return daq_dlt; -} - -bool SFDAQInstance::can_inject() -{ - return (daq_get_capabilities(daq_mod, daq_hand) & DAQ_CAPA_INJECT) != 0; -} - -bool SFDAQInstance::can_inject_raw() -{ - return (daq_get_capabilities(daq_mod, daq_hand) & DAQ_CAPA_INJECT_RAW) != 0; -} - -bool SFDAQInstance::can_replace() -{ - return (daq_get_capabilities(daq_mod, daq_hand) & DAQ_CAPA_REPLACE) != 0; -} - -bool SFDAQInstance::can_retry() -{ - return (daq_get_capabilities(daq_mod, daq_hand) & DAQ_CAPA_RETRY) != 0; -} - -bool SFDAQInstance::can_start_unprivileged() -{ - return (daq_get_capabilities(daq_mod, daq_hand) & DAQ_CAPA_UNPRIV_START) != 0; -} - -bool SFDAQInstance::can_whitelist() -{ - return (daq_get_capabilities(daq_mod, daq_hand) & DAQ_CAPA_WHITELIST) != 0; -} - -bool SFDAQInstance::set_filter(const char* bpf) -{ - int err = 0; - - // The BPF can be compiled either during daq_set_filter() or daq_start(), - // so protect the thread-unsafe BPF scanner/compiler in both places. - - if (bpf and *bpf) - { - std::lock_guard lock(bpf_gate); - err = daq_set_filter(daq_mod, daq_hand, bpf); - } - - if (err) - FatalError("Can't set DAQ BPF filter to '%s' (%s)\n", - bpf, daq_get_error(daq_mod, daq_hand)); - - return (err == DAQ_SUCCESS); -} - -bool SFDAQInstance::start() -{ - int err; - - // The BPF can be compiled either during daq_set_filter() or daq_start(), - // so protect the thread-unsafe BPF scanner/compiler in both places. - { - std::lock_guard lock(bpf_gate); - err = daq_start(daq_mod, daq_hand); - } - - if (err) - ErrorMessage("Can't start DAQ (%d) - %s\n", err, daq_get_error(daq_mod, daq_hand)); - else - daq_dlt = daq_get_datalink_type(daq_mod, daq_hand); - - get_tunnel_capabilities(); - - return (err == DAQ_SUCCESS); -} - -void SFDAQInstance::get_tunnel_capabilities() -{ - daq_tunnel_mask = 0; - if (daq_mod && daq_hand) - { - uint32_t caps = daq_get_capabilities(daq_mod, daq_hand); - - if (caps & DAQ_CAPA_DECODE_GTP) - { - daq_tunnel_mask |= TUNNEL_GTP; - } - if (caps & DAQ_CAPA_DECODE_TEREDO) - { - daq_tunnel_mask |= TUNNEL_TEREDO; - } - if (caps & DAQ_CAPA_DECODE_GRE) - { - daq_tunnel_mask |= TUNNEL_GRE; - } - if (caps & DAQ_CAPA_DECODE_4IN4) - { - daq_tunnel_mask |= TUNNEL_4IN4; - } - if (caps & DAQ_CAPA_DECODE_6IN4) - { - daq_tunnel_mask |= TUNNEL_6IN4; - } - if (caps & DAQ_CAPA_DECODE_4IN6) - { - daq_tunnel_mask |= TUNNEL_4IN6; - } - if (caps & DAQ_CAPA_DECODE_6IN6) - { - daq_tunnel_mask |= TUNNEL_6IN6; - } - if (caps & DAQ_CAPA_DECODE_MPLS) - { - daq_tunnel_mask |= TUNNEL_MPLS; - } - } -} - -bool SFDAQInstance::get_tunnel_bypass(uint8_t proto) -{ - return (daq_tunnel_mask & proto) != 0; -} - -bool SFDAQInstance::was_started() -{ - DAQ_State s; - - if (!daq_hand) - return false; - - s = daq_check_status(daq_mod, daq_hand); - - return (DAQ_STATE_STARTED == s); -} - -bool SFDAQInstance::stop() -{ - int err = daq_stop(daq_mod, daq_hand); - - if (err) - LogMessage("Can't stop DAQ (%d) - %s\n", err, daq_get_error(daq_mod, daq_hand)); - - return (err == DAQ_SUCCESS); -} - -static int metacallback(void *user, const DAQ_MetaHdr_t* hdr, const uint8_t* data) -{ - DataBus::publish(DAQ_META_EVENT, user, hdr->type, data); - return 0; -} - -int SFDAQInstance::acquire(int max, DAQ_Analysis_Func_t callback) -{ - int err = daq_acquire_with_meta(daq_mod, daq_hand, max, callback, metacallback, nullptr); - - if (err && err != DAQ_READFILE_EOF) - LogMessage("Can't acquire (%d) - %s\n", err, daq_get_error(daq_mod, daq_hand)); - - if (s_error != DAQ_SUCCESS) - { - err = s_error; - s_error = DAQ_SUCCESS; - } - return err; -} - -int SFDAQInstance::inject(const DAQ_PktHdr_t* h, int rev, const uint8_t* buf, uint32_t len) -{ - int err = daq_inject(daq_mod, daq_hand, h, buf, len, rev); -#ifdef DEBUG_MSGS - if (err) - LogMessage("Can't inject (%d) - %s\n", err, daq_get_error(daq_mod, daq_hand)); -#endif - return err; -} - -bool SFDAQInstance::break_loop(int error) -{ - if (error) - s_error = error; - return (daq_breakloop(daq_mod, daq_hand) == DAQ_SUCCESS); -} - -// returns statically allocated stats - don't free -const DAQ_Stats_t* SFDAQInstance::get_stats() -{ - if (daq_hand) - { - int err = daq_get_stats(daq_mod, daq_hand, &daq_stats); - - if (err) - LogMessage("Can't get DAQ stats (%d) - %s\n", err, daq_get_error(daq_mod, daq_hand)); - - // Some DAQs don't provide hw numbers, so we default HW RX to the SW equivalent - // (this means outstanding packets = 0) - if (!daq_stats.hw_packets_received) - daq_stats.hw_packets_received = daq_stats.packets_received + daq_stats.packets_filtered; - } - - return &daq_stats; -} - -int SFDAQInstance::query_flow(const DAQ_PktHdr_t* hdr, DAQ_QueryFlow_t* query) -{ - return daq_query_flow(daq_mod, daq_hand, hdr, query); -} - -int SFDAQInstance::modify_flow_opaque(const DAQ_PktHdr_t* hdr, uint32_t opaque) -{ - DAQ_ModFlow_t mod; - - mod.type = DAQ_MODFLOW_TYPE_OPAQUE; - mod.length = sizeof(opaque); - mod.value = &opaque; - - return daq_modify_flow(daq_mod, daq_hand, hdr, &mod); -} - -int SFDAQInstance::modify_flow_pkt_trace(const DAQ_PktHdr_t* hdr, uint8_t verdict_reason, - uint8_t* buff, uint32_t buff_len) -{ - DAQ_ModFlow_t mod; - DAQ_ModFlowPktTrace_t mod_tr; - mod_tr.vreason = verdict_reason; - mod_tr.pkt_trace_data_len = buff_len; - mod_tr.pkt_trace_data = buff; - mod.type = DAQ_MODFLOW_TYPE_PKT_TRACE; - mod.length = sizeof(DAQ_ModFlowPktTrace_t); - mod.value = (void*)&mod_tr; - return daq_modify_flow(daq_mod, daq_hand, hdr, &mod); -} - -// FIXIT-L X Add Snort flag definitions for callers to use and translate/pass them through to -// the DAQ module -int SFDAQInstance::add_expected(const Packet* ctrlPkt, const SfIp* cliIP, uint16_t cliPort, - const SfIp* srvIP, uint16_t srvPort, IpProtocol protocol, unsigned timeout_ms, unsigned /* flags */) -{ - DAQ_Data_Channel_Params_t daq_params; - DAQ_DP_key_t dp_key; - - dp_key.src_af = cliIP->get_family(); - if (cliIP->is_ip4()) - dp_key.sa.src_ip4.s_addr = cliIP->get_ip4_value(); - else - memcpy(&dp_key.sa.src_ip6, cliIP->get_ip6_ptr(), sizeof(dp_key.sa.src_ip6)); - dp_key.src_port = cliPort; - - dp_key.dst_af = srvIP->get_family(); - if (srvIP->is_ip4()) - dp_key.da.dst_ip4.s_addr = srvIP->get_ip4_value(); - else - memcpy(&dp_key.da.dst_ip6, srvIP->get_ip6_ptr(), sizeof(dp_key.da.dst_ip6)); - dp_key.dst_port = srvPort; - - dp_key.protocol = (uint8_t) protocol; - dp_key.vlan_cnots = 1; - if (ctrlPkt->proto_bits & PROTO_BIT__VLAN) - dp_key.vlan_id = layer::get_vlan_layer(ctrlPkt)->vid(); - else - dp_key.vlan_id = 0xFFFF; - - if (ctrlPkt->proto_bits & PROTO_BIT__GTP) - dp_key.tunnel_type = DAQ_DP_TUNNEL_TYPE_GTP_TUNNEL; - else if (ctrlPkt->proto_bits & PROTO_BIT__MPLS) - dp_key.tunnel_type = DAQ_DP_TUNNEL_TYPE_MPLS_TUNNEL; -/* - else if ( ctrlPkt->encapsulated ) - dp_key.tunnel_type = DAQ_DP_TUNNEL_TYPE_OTHER_TUNNEL; -*/ - else - dp_key.tunnel_type = DAQ_DP_TUNNEL_TYPE_NON_TUNNEL; - - memset(&daq_params, 0, sizeof(daq_params)); - daq_params.timeout_ms = timeout_ms; -/* - if (flags & DAQ_DC_FLOAT) - daq_params.flags |= DAQ_DATA_CHANNEL_FLOAT; - if (flags & DAQ_DC_ALLOW_MULTIPLE) - daq_params.flags |= DAQ_DATA_CHANNEL_ALLOW_MULTIPLE; - if (flags & DAQ_DC_PERSIST) - daq_params.flags |= DAQ_DATA_CHANNEL_PERSIST; -*/ - - return daq_dp_add_dc(daq_mod, daq_hand, ctrlPkt->pkth, &dp_key, nullptr, &daq_params); -} diff --git a/src/packet_io/sfdaq.h b/src/packet_io/sfdaq.h index 2fa4613c9..d207c2f81 100644 --- a/src/packet_io/sfdaq.h +++ b/src/packet_io/sfdaq.h @@ -24,101 +24,48 @@ #include -#include +#include #include "main/snort_types.h" -#include "protocols/protocol_ids.h" -namespace snort -{ -struct Packet; -struct SnortConfig; -struct SfIp; +struct SFDAQConfig; -class SFDAQInstance +namespace snort { -public: - SFDAQInstance(const char* intf); - ~SFDAQInstance(); - - bool configure(const SnortConfig*); - void set_metacallback(DAQ_Meta_Func_t); - - bool start(); - bool was_started(); - bool stop(); - void reload(); - void abort(); - - int get_base_protocol(); - const char* get_interface_spec(); - const DAQ_Stats_t* get_stats(); - - bool can_inject(); - bool can_inject_raw(); - bool can_replace(); - bool can_retry(); - bool can_start_unprivileged(); - SO_PUBLIC bool can_whitelist(); - - int acquire(int max, DAQ_Analysis_Func_t); - int inject(const DAQ_PktHdr_t*, int rev, const uint8_t* buf, uint32_t len); - bool break_loop(int error); - - SO_PUBLIC int query_flow(const DAQ_PktHdr_t*, DAQ_QueryFlow_t*); - SO_PUBLIC int modify_flow_opaque(const DAQ_PktHdr_t*, uint32_t opaque); - int modify_flow_pkt_trace(const DAQ_PktHdr_t*, uint8_t verdict_reason, - uint8_t* buff, uint32_t buff_len); - int add_expected(const Packet* ctrlPkt, const SfIp* cliIP, uint16_t cliPort, - const SfIp* srvIP, uint16_t srvPort, IpProtocol, unsigned timeout_ms, - unsigned /* flags */); - bool get_tunnel_bypass(uint8_t proto); - -private: - void get_tunnel_capabilities(); - bool set_filter(const char*); - std::string interface_spec; - void* daq_hand; - int daq_dlt; - int s_error; - DAQ_Stats_t daq_stats; - uint8_t daq_tunnel_mask; -}; +class SFDAQInstance; class SFDAQ { public: - static void load(const SnortConfig*); + static void load(const SFDAQConfig*); static void unload(); static void print_types(std::ostream&); static const char* verdict_to_string(DAQ_Verdict verdict); - static void init(const SnortConfig*); + static bool init(const SFDAQConfig*); static void term(); - static const char* get_type(); - static const char* get_input_spec(const SnortConfig*, unsigned instance_id); + static bool init_instance(SFDAQInstance*, const std::string& bpf_string); + + static const char* get_input_spec(const SFDAQConfig*, unsigned instance_id); static const char* default_type(); static const DAQ_Stats_t* get_stats(); - static bool unprivileged(); static bool can_inject(); static bool can_inject_raw(); static bool can_replace(); - static bool can_retry(); + static bool can_run_unprivileged(); SO_PUBLIC static bool get_tunnel_bypass(uint8_t proto); // FIXIT-M X Temporary thread-local instance helpers to be removed when no longer needed static void set_local_instance(SFDAQInstance*); SO_PUBLIC static SFDAQInstance* get_local_instance(); - SO_PUBLIC static const char* get_interface_spec(); + SO_PUBLIC static const char* get_input_spec(); SO_PUBLIC static int get_base_protocol(); - SO_PUBLIC static uint32_t get_snap_len(); - static int inject(const DAQ_PktHdr_t*, int rev, const uint8_t* buf, uint32_t len); + static int inject(DAQ_Msg_h, int rev, const uint8_t* buf, uint32_t len); static bool forwarding_packet(const DAQ_PktHdr_t*); - static bool break_loop(int error); }; } #endif diff --git a/src/packet_io/sfdaq_config.cc b/src/packet_io/sfdaq_config.cc index 44d1d9ec6..bf16400bb 100644 --- a/src/packet_io/sfdaq_config.cc +++ b/src/packet_io/sfdaq_config.cc @@ -26,8 +26,6 @@ using namespace std; -static const unsigned DEFAULT_PKT_TIMEOUT = 1000; // ms, worst daq resolution is 1 sec - static pair parse_variable(const char* varkvp) { string key = varkvp; @@ -44,27 +42,22 @@ static pair parse_variable(const char* varkvp) } /* - * SFDAQConfigInstance + * SFDAQModuleConfig */ -SFDAQInstanceConfig::SFDAQInstanceConfig(const SFDAQInstanceConfig& other) +SFDAQModuleConfig::SFDAQModuleConfig(const SFDAQModuleConfig& other) { - input_spec = other.input_spec; + name = other.name; + mode = other.mode; variables = other.variables; } -void SFDAQInstanceConfig::set_input_spec(const char* input_spec_str) +void SFDAQModuleConfig::set_variable(const char* varkvp) { - if (input_spec_str) - input_spec = input_spec_str; - else - input_spec.clear(); + if (varkvp) + variables.emplace_back(parse_variable(varkvp)); } -void SFDAQInstanceConfig::set_variable(const char* varkvp) -{ - variables.emplace_back(parse_variable(varkvp)); -} /* * SFDAQConfig @@ -72,78 +65,45 @@ void SFDAQInstanceConfig::set_variable(const char* varkvp) SFDAQConfig::SFDAQConfig() { - mru_size = -1; - timeout = DEFAULT_PKT_TIMEOUT; + batch_size = BATCH_SIZE_UNSET; + mru_size = SNAPLEN_UNSET; + timeout = TIMEOUT_DEFAULT; } SFDAQConfig::~SFDAQConfig() { - for (auto it : instances) - delete it.second; + for (auto it : module_configs) + delete it; } -void SFDAQConfig::add_module_dir(const char* module_dir) +void SFDAQConfig::add_input(const char* input) { - if (module_dir) - module_dirs.emplace_back(module_dir); + if (input) + inputs.emplace_back(input); } -void SFDAQConfig::set_input_spec(const char* input_spec_str, int instance_id) +SFDAQModuleConfig* SFDAQConfig::add_module_config(const char* module_name) { - if (instance_id >= 0) - { - SFDAQInstanceConfig* ic; - - auto it = instances.find(instance_id); - if (it == instances.end()) - { - ic = new SFDAQInstanceConfig; - instances[instance_id] = ic; - } - else - ic = it->second; - - ic->set_input_spec(input_spec_str); - } - else - { - if (input_spec_str) - input_spec = input_spec_str; - else - input_spec.clear(); - } + SFDAQModuleConfig* modcfg = new SFDAQModuleConfig(); + modcfg->name = module_name; + module_configs.emplace_back(modcfg); + return modcfg; } -void SFDAQConfig::set_module_name(const char* module_name_str) +void SFDAQConfig::add_module_dir(const char* module_dir) { - if (module_name_str) - module_name = module_name_str; + if (module_dir) + module_dirs.emplace_back(module_dir); } -void SFDAQConfig::set_mru_size(int mru_size_value) +void SFDAQConfig::set_batch_size(uint32_t batch_size_value) { - mru_size = mru_size_value; + batch_size = batch_size_value; } -void SFDAQConfig::set_variable(const char* varkvp, int instance_id) +void SFDAQConfig::set_mru_size(int mru_size_value) { - if (instance_id >= 0) - { - SFDAQInstanceConfig* ic; - - auto it = instances.find(instance_id); - if (it == instances.end()) - { - ic = new SFDAQInstanceConfig; - instances[instance_id] = ic; - } - else - ic = it->second; - - ic->set_variable(varkvp); - } - else - variables.emplace_back(parse_variable(varkvp)); + mru_size = mru_size_value; } void SFDAQConfig::overlay(const SFDAQConfig* other) @@ -151,35 +111,21 @@ void SFDAQConfig::overlay(const SFDAQConfig* other) if (!other->module_dirs.empty()) module_dirs = other->module_dirs; - if (!other->module_name.empty()) - module_name = other->module_name; - - if (!other->input_spec.empty()) - input_spec = other->input_spec; + if (!other->module_configs.empty()) + { + for (SFDAQModuleConfig *dmc : module_configs) + delete dmc; + module_configs.clear(); + for (SFDAQModuleConfig *dmc : other->module_configs) + module_configs.emplace_back(new SFDAQModuleConfig(*dmc)); + } - if (!other->variables.empty()) - variables = other->variables; + if (!other->inputs.empty()) + inputs = other->inputs; - if (other->mru_size != -1) + if (other->batch_size != BATCH_SIZE_UNSET) + batch_size = other->batch_size; + if (other->mru_size != SNAPLEN_UNSET) mru_size = other->mru_size; - - for (auto oit = other->instances.begin(); oit != other->instances.end(); oit++) - { - SFDAQInstanceConfig* oic = oit->second; - SFDAQInstanceConfig* ic; - auto it = instances.find(oit->first); - if (it != instances.end()) - { - ic = it->second; - if (!oic->input_spec.empty()) - ic->input_spec = oic->input_spec; - if (!oic->variables.empty()) - ic->variables = oic->variables; - } - else - { - ic = new SFDAQInstanceConfig(*oic); - instances[oit->first] = ic; - } - } + timeout = other->timeout; } diff --git a/src/packet_io/sfdaq_config.h b/src/packet_io/sfdaq_config.h index 1665f4017..abf573b9a 100644 --- a/src/packet_io/sfdaq_config.h +++ b/src/packet_io/sfdaq_config.h @@ -25,19 +25,27 @@ #include #include -/* Per-Instance override configuration */ -struct SFDAQInstanceConfig -{ - SFDAQInstanceConfig() = default; - SFDAQInstanceConfig(const SFDAQInstanceConfig&); +using DaqVar = std::pair; +using DaqVarList = std::vector; - SFDAQInstanceConfig& operator=(const SFDAQInstanceConfig&) = delete; +/* Module configuration */ +struct SFDAQModuleConfig +{ + enum SFDAQMode + { + SFDAQ_MODE_UNSET, + SFDAQ_MODE_PASSIVE, + SFDAQ_MODE_INLINE, + SFDAQ_MODE_READ_FILE, + }; - void set_input_spec(const char*); + SFDAQModuleConfig() = default; + SFDAQModuleConfig(const SFDAQModuleConfig&); void set_variable(const char* varkvp); - std::string input_spec; - std::vector> variables; + std::string name; + SFDAQMode mode = SFDAQ_MODE_UNSET; + DaqVarList variables; }; /* General/base configuration */ @@ -46,23 +54,32 @@ struct SFDAQConfig SFDAQConfig(); ~SFDAQConfig(); + void add_input(const char*); + SFDAQModuleConfig* add_module_config(const char* module_name); void add_module_dir(const char*); - void set_input_spec(const char*, int instance_id = -1); - void set_module_name(const char*); + void set_batch_size(uint32_t); void set_mru_size(int); - void set_variable(const char* varkvp, int instance_id = -1); + + uint32_t get_batch_size() const { return (batch_size == BATCH_SIZE_UNSET) ? BATCH_SIZE_DEFAULT : batch_size; } + uint32_t get_mru_size() const { return (mru_size == SNAPLEN_UNSET) ? SNAPLEN_DEFAULT : mru_size; } void overlay(const SFDAQConfig*); /* General configuration */ std::vector module_dirs; - std::string module_name; - /* Module configuration */ - std::string input_spec; - std::vector> variables; + /* Instance configuration */ + std::vector inputs; + uint32_t batch_size; int mru_size; unsigned int timeout; - std::unordered_map instances; + std::vector module_configs; + + /* Constants */ + static constexpr uint32_t BATCH_SIZE_UNSET = 0; + static constexpr int SNAPLEN_UNSET = -1; + static constexpr uint32_t BATCH_SIZE_DEFAULT = 64; + static constexpr int SNAPLEN_DEFAULT = 1518; + static constexpr unsigned TIMEOUT_DEFAULT = 1000; }; #endif diff --git a/src/packet_io/sfdaq_instance.cc b/src/packet_io/sfdaq_instance.cc new file mode 100644 index 000000000..9f889b088 --- /dev/null +++ b/src/packet_io/sfdaq_instance.cc @@ -0,0 +1,388 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- + +// sfdaq_instance.cc author Michael Altizer + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "sfdaq_instance.h" + +#include + +#include "log/messages.h" +#include "main/snort_config.h" +#include "protocols/packet.h" +#include "protocols/vlan.h" + +#include "sfdaq_config.h" + +using namespace snort; + +SFDAQInstance::SFDAQInstance(const char* input, const SFDAQConfig* cfg) +{ + if (input) + input_spec = input; + batch_size = cfg->get_batch_size(); + daq_msgs = new DAQ_Msg_h[batch_size]; +} + +SFDAQInstance::~SFDAQInstance() +{ + delete[] daq_msgs; + if (instance) + daq_instance_destroy(instance); +} + +static bool DAQ_ValidateInstance(DAQ_Instance_h instance) +{ + uint32_t caps = daq_instance_get_capabilities(instance); + + if (!SnortConfig::adaptor_inline_mode()) + return true; + + if (!(caps & DAQ_CAPA_BLOCK)) + ParseWarning(WARN_DAQ, "inline mode configured but DAQ can't block packets.\n"); + + return true; +} + +bool SFDAQInstance::init(DAQ_Config_h daqcfg, const std::string& bpf_string) +{ + char buf[256] = ""; + int rval; + + /* Reuse the main DAQ instance configuration with the input specification specific to this instance. */ + daq_config_set_input(daqcfg, input_spec.c_str()); + if ((rval = daq_instance_instantiate(daqcfg, &instance, buf, sizeof(buf))) != DAQ_SUCCESS) + { + ErrorMessage("Couldn't construct a DAQ instance: %s (%d)\n", buf, rval); + return false; + } + + if (!DAQ_ValidateInstance(instance)) + FatalError("DAQ configuration incompatible with intended operation.\n"); + + if (!bpf_string.empty()) + { + rval = daq_instance_set_filter(instance, bpf_string.c_str()); + if (rval != DAQ_SUCCESS) + FatalError("Couldn't set DAQ instance BPF filter to '%s': %s (%d)\n", + bpf_string.c_str(), daq_instance_get_error(instance), rval); + } + + return true; +} + +void SFDAQInstance::reload() +{ + if (!instance) + return; + + void* new_config = nullptr; + if (daq_instance_config_load(instance, &new_config) == DAQ_SUCCESS) + { + void* old_config = nullptr; + if (daq_instance_config_swap(instance, new_config, &old_config) == DAQ_SUCCESS) + daq_instance_config_free(instance, old_config); + else + daq_instance_config_free(instance, new_config); + } +} + +void SFDAQInstance::abort() +{ + if (was_started()) + stop(); + + //DAQ_Delete(); + //DAQ_Term(); FIXIT-L this must be called from main thread on abort +} + +const char* SFDAQInstance::get_input_spec() +{ + return input_spec.c_str(); +} + +// That distinction does not hold with datalink types. Snort must use whatever +// datalink type the DAQ coughs up as its base protocol decoder. For pcaps, +// the datalink type in the file must be used - which may not be known until +// start. The value is cached here since it used for packet operations like +// logging and is needed at shutdown. This avoids sequencing issues. +int SFDAQInstance::get_base_protocol() +{ + return dlt; +} + +bool SFDAQInstance::can_inject() +{ + return (daq_instance_get_capabilities(instance) & DAQ_CAPA_INJECT) != 0; +} + +bool SFDAQInstance::can_inject_raw() +{ + return (daq_instance_get_capabilities(instance) & DAQ_CAPA_INJECT_RAW) != 0; +} + +bool SFDAQInstance::can_replace() +{ + return (daq_instance_get_capabilities(instance) & DAQ_CAPA_REPLACE) != 0; +} + +bool SFDAQInstance::can_start_unprivileged() +{ + return (daq_instance_get_capabilities(instance) & DAQ_CAPA_UNPRIV_START) != 0; +} + +bool SFDAQInstance::can_whitelist() +{ + return (daq_instance_get_capabilities(instance) & DAQ_CAPA_WHITELIST) != 0; +} + +bool SFDAQInstance::start() +{ + int rval = daq_instance_start(instance); + if (rval != DAQ_SUCCESS) + { + ErrorMessage("Couldn't start DAQ instance: %s (%d)\n", daq_instance_get_error(instance), rval); + return false; + } + + DAQ_MsgPoolInfo_t mpool_info; + rval = daq_instance_get_msg_pool_info(instance, &mpool_info); + if (rval != DAQ_SUCCESS) + { + ErrorMessage("Couldn't query DAQ message pool info: %s (%d)\n", daq_instance_get_error(instance), rval); + stop(); + return false; + } + pool_size = mpool_info.size; + pool_available = mpool_info.available; + assert(pool_size == pool_available); + + dlt = daq_instance_get_datalink_type(instance); + get_tunnel_capabilities(); + + return (rval == DAQ_SUCCESS); +} + +DAQ_RecvStatus SFDAQInstance::receive_messages(unsigned max_recv) +{ + assert(max_recv <= batch_size); + + if (max_recv > pool_available) + max_recv = pool_available; + + DAQ_RecvStatus rstat; + curr_batch_size = daq_instance_msg_receive(instance, max_recv, daq_msgs, &rstat); + pool_available -= curr_batch_size; + curr_batch_idx = 0; + + return rstat; +} + +int SFDAQInstance::finalize_message(DAQ_Msg_h msg, DAQ_Verdict verdict) +{ + int rval = daq_instance_msg_finalize(instance, msg, verdict); + if (rval == DAQ_SUCCESS) + pool_available++; + return rval; +} + +const char* SFDAQInstance::get_error() +{ + return daq_instance_get_error(instance); +} + +void SFDAQInstance::get_tunnel_capabilities() +{ + daq_tunnel_mask = 0; + if (instance) + { + uint32_t caps = daq_instance_get_capabilities(instance); + + if (caps & DAQ_CAPA_DECODE_GTP) + daq_tunnel_mask |= TUNNEL_GTP; + if (caps & DAQ_CAPA_DECODE_TEREDO) + daq_tunnel_mask |= TUNNEL_TEREDO; + if (caps & DAQ_CAPA_DECODE_GRE) + daq_tunnel_mask |= TUNNEL_GRE; + if (caps & DAQ_CAPA_DECODE_4IN4) + daq_tunnel_mask |= TUNNEL_4IN4; + if (caps & DAQ_CAPA_DECODE_6IN4) + daq_tunnel_mask |= TUNNEL_6IN4; + if (caps & DAQ_CAPA_DECODE_4IN6) + daq_tunnel_mask |= TUNNEL_4IN6; + if (caps & DAQ_CAPA_DECODE_6IN6) + daq_tunnel_mask |= TUNNEL_6IN6; + if (caps & DAQ_CAPA_DECODE_MPLS) + daq_tunnel_mask |= TUNNEL_MPLS; + } +} + +bool SFDAQInstance::get_tunnel_bypass(uint8_t proto) +{ + return (daq_tunnel_mask & proto) != 0; +} + +bool SFDAQInstance::was_started() +{ + DAQ_State s; + + if (!instance) + return false; + + s = daq_instance_check_status(instance); + + return (DAQ_STATE_STARTED == s); +} + +bool SFDAQInstance::stop() +{ + assert(pool_size == pool_available); + + int rval = daq_instance_stop(instance); + + if (rval != DAQ_SUCCESS) + LogMessage("Couldn't stop DAQ instance: %s (%d)\n", daq_instance_get_error(instance), rval); + + return (rval == DAQ_SUCCESS); +} + +int SFDAQInstance::inject(DAQ_Msg_h msg, int rev, const uint8_t* buf, uint32_t len) +{ + int rval = daq_instance_inject_relative(instance, msg, buf, len, rev); +#ifdef DEBUG_MSGS + if (rval != DAQ_SUCCESS) + LogMessage("Couldn't inject on DAQ instance: %s (%d)\n", daq_instance_get_error(instance), rval); +#endif + return rval; +} + +bool SFDAQInstance::interrupt() +{ + return (daq_instance_interrupt(instance) == DAQ_SUCCESS); +} + +const DAQ_Stats_t* SFDAQInstance::get_stats() +{ + if (instance) + { + int rval = daq_instance_get_stats(instance, &daq_stats); + if (rval != DAQ_SUCCESS) + LogMessage("Couldn't query DAQ stats: %s (%d)\n", daq_instance_get_error(instance), rval); + + // Some DAQ modules don't provide hardware numbers, so we default HW RX to the SW equivalent + // (this means outstanding packets = 0) + if (daq_stats.hw_packets_received == 0) + daq_stats.hw_packets_received = daq_stats.packets_received + daq_stats.packets_filtered; + } + + return &daq_stats; +} + +int SFDAQInstance::ioctl(DAQ_IoctlCmd cmd, void *arg, size_t arglen) +{ + return daq_instance_ioctl(instance, cmd, arg, arglen); +} + +int SFDAQInstance::modify_flow_opaque(DAQ_Msg_h msg, uint32_t opaque) +{ + DIOCTL_SetFlowOpaque d_sfo; + d_sfo.msg = msg; + d_sfo.value = opaque; + + return daq_instance_ioctl(instance, DIOCTL_SET_FLOW_OPAQUE, &d_sfo, sizeof(d_sfo)); +} + +int SFDAQInstance::modify_flow_pkt_trace(DAQ_Msg_h msg, uint8_t verdict_reason, uint8_t* buff, uint32_t buff_len) +{ + DIOCTL_SetPacketTraceData d_sptd; + + d_sptd.msg = msg; + d_sptd.verdict_reason = verdict_reason; + d_sptd.trace_data_len = buff_len; + d_sptd.trace_data = buff; + + return daq_instance_ioctl(instance, DIOCTL_SET_PACKET_TRACE_DATA, &d_sptd, sizeof(d_sptd)); +} + +// FIXIT-L X Add Snort flag definitions for callers to use and translate/pass them through to +// the DAQ module +int SFDAQInstance::add_expected(const Packet* ctrlPkt, const SfIp* cliIP, uint16_t cliPort, + const SfIp* srvIP, uint16_t srvPort, IpProtocol protocol, unsigned timeout_ms, unsigned /* flags */) +{ + DIOCTL_CreateExpectedFlow d_cef; + + d_cef.ctrl_msg = ctrlPkt->daq_msg; + + /* Populate the expected flow key */ + DAQ_EFlow_Key_t* key = &d_cef.key; + + key->src_af = cliIP->get_family(); + if (cliIP->is_ip4()) + key->sa.src_ip4.s_addr = cliIP->get_ip4_value(); + else + memcpy(&key->sa.src_ip6, cliIP->get_ip6_ptr(), sizeof(key->sa.src_ip6)); + key->src_port = cliPort; + + key->dst_af = srvIP->get_family(); + if (srvIP->is_ip4()) + key->da.dst_ip4.s_addr = srvIP->get_ip4_value(); + else + memcpy(&key->da.dst_ip6, srvIP->get_ip6_ptr(), sizeof(key->da.dst_ip6)); + key->dst_port = srvPort; + + // FIXIT-M The key address_space_id is not currently being populated! + + if (ctrlPkt->proto_bits & PROTO_BIT__GTP) + key->tunnel_type = DAQ_EFLOW_TUNNEL_TYPE_GTP_TUNNEL; + else if (ctrlPkt->proto_bits & PROTO_BIT__MPLS) + key->tunnel_type = DAQ_EFLOW_TUNNEL_TYPE_MPLS_TUNNEL; + // FIXIT-L Need to figure out the right way to determine "Other" encapsulation +/* + else if ( ctrlPkt->encapsulated ) + key->tunnel_type = DAQ_EFLOW_TUNNEL_TYPE_OTHER_TUNNEL; +*/ + else + key->tunnel_type = DAQ_EFLOW_TUNNEL_TYPE_NON_TUNNEL; + + key->protocol = (uint8_t) protocol; + if (ctrlPkt->proto_bits & PROTO_BIT__VLAN) + key->vlan_id = layer::get_vlan_layer(ctrlPkt)->vid(); + else + key->vlan_id = 0xFFFF; + key->vlan_cnots = 1; + + d_cef.flags = 0; +/* + if (flags & DAQ_DC_FLOAT) + d_cef.flags |= DAQ_EFLOW_FLOAT; + if (flags & DAQ_DC_ALLOW_MULTIPLE) + d_cef.flags |= DAQ_EFLOW_ALLOW_MULTIPLE; + if (flags & DAQ_DC_PERSIST) + d_cef.flags |= DAQ_EFLOW_PERSIST; +*/ + d_cef.timeout_ms = timeout_ms; + // Opaque data blob for expected flows is currently unused/unimplemented + d_cef.data = nullptr; + d_cef.length = 0; + + return daq_instance_ioctl(instance, DIOCTL_CREATE_EXPECTED_FLOW, &d_cef, sizeof(d_cef)); +} diff --git a/src/packet_io/sfdaq_instance.h b/src/packet_io/sfdaq_instance.h new file mode 100644 index 000000000..bc256d889 --- /dev/null +++ b/src/packet_io/sfdaq_instance.h @@ -0,0 +1,103 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2014-2019 Cisco and/or its affiliates. All rights reserved. +// Copyright (C) 2005-2013 Sourcefire, Inc. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- + +// sfdaq.h author Michael Altizer + +#ifndef SFDAQ_INSTANCE_H +#define SFDAQ_INSTANCE_H + +#include + +#include + +#include "main/snort_types.h" +#include "protocols/protocol_ids.h" + +struct SFDAQConfig; + +namespace snort +{ +struct Packet; +struct SfIp; + +class SFDAQInstance +{ +public: + SFDAQInstance(const char* intf, const SFDAQConfig*); + ~SFDAQInstance(); + + bool init(DAQ_Config_h, const std::string& bpf_string); + + bool start(); + bool was_started(); + bool stop(); + void reload(); + void abort(); + + DAQ_RecvStatus receive_messages(unsigned max_recv); + DAQ_Msg_h next_message() + { + if (curr_batch_idx < curr_batch_size) + return daq_msgs[curr_batch_idx++]; + return nullptr; + } + int finalize_message(DAQ_Msg_h msg, DAQ_Verdict verdict); + const char* get_error(); + + int get_base_protocol(); + uint32_t get_batch_size() { return batch_size; } + const char* get_input_spec(); + const DAQ_Stats_t* get_stats(); + + bool can_inject(); + bool can_inject_raw(); + bool can_replace(); + bool can_start_unprivileged(); + SO_PUBLIC bool can_whitelist(); + + int inject(DAQ_Msg_h, int rev, const uint8_t* buf, uint32_t len); + bool interrupt(); + + SO_PUBLIC int ioctl(DAQ_IoctlCmd cmd, void *arg, size_t arglen); + SO_PUBLIC int modify_flow_opaque(DAQ_Msg_h, uint32_t opaque); + int modify_flow_pkt_trace(DAQ_Msg_h, uint8_t verdict_reason, + uint8_t* buff, uint32_t buff_len); + int add_expected(const Packet* ctrlPkt, const SfIp* cliIP, uint16_t cliPort, + const SfIp* srvIP, uint16_t srvPort, IpProtocol, unsigned timeout_ms, + unsigned /* flags */); + bool get_tunnel_bypass(uint8_t proto); + +private: + void get_tunnel_capabilities(); + + std::string input_spec; + DAQ_Instance_h instance = nullptr; + DAQ_Msg_h* daq_msgs; + unsigned curr_batch_size = 0; + unsigned curr_batch_idx = 0; + uint32_t batch_size; + uint32_t pool_size = 0; + uint32_t pool_available = 0; + int dlt = -1; + DAQ_Stats_t daq_stats = { }; + uint8_t daq_tunnel_mask = 0; +}; +} +#endif + diff --git a/src/packet_io/sfdaq_module.cc b/src/packet_io/sfdaq_module.cc index bb3261ab4..c15cf9c20 100644 --- a/src/packet_io/sfdaq_module.cc +++ b/src/packet_io/sfdaq_module.cc @@ -38,78 +38,47 @@ using namespace snort; #define sfdaq_help "configure packet acquisition interface" -struct DAQStats +/* + * Module Configuration + */ + +static const Parameter daqvar_list_param[] = { - PegCount pcaps; - PegCount received; - PegCount analyzed; - PegCount dropped; - PegCount filtered; - PegCount outstanding; - PegCount injected; - PegCount verdicts[MAX_DAQ_VERDICT]; - PegCount internal_blacklist; - PegCount internal_whitelist; - PegCount skipped; - PegCount idle; - PegCount rx_bytes; + { "variable", Parameter::PT_STRING, nullptr, nullptr, "DAQ module variable (foo[=bar])" }, + + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; -const PegInfo daq_names[] = +static const Parameter daq_module_param[] = { - { CountType::MAX, "pcaps", "total files and interfaces processed" }, - { CountType::SUM, "received", "total packets received from DAQ" }, - { CountType::SUM, "analyzed", "total packets analyzed from DAQ" }, - { CountType::SUM, "dropped", "packets dropped" }, - { CountType::SUM, "filtered", "packets filtered out" }, - { CountType::SUM, "outstanding", "packets unprocessed" }, - { CountType::SUM, "injected", "active responses or replacements" }, - { CountType::SUM, "allow", "total allow verdicts" }, - { CountType::SUM, "block", "total block verdicts" }, - { CountType::SUM, "replace", "total replace verdicts" }, - { CountType::SUM, "whitelist", "total whitelist verdicts" }, - { CountType::SUM, "blacklist", "total blacklist verdicts" }, - { CountType::SUM, "ignore", "total ignore verdicts" }, - { CountType::SUM, "retry", "total retry verdicts" }, + { "name", Parameter::PT_STRING, nullptr, nullptr, "DAQ module name (required)" }, + { "mode", Parameter::PT_ENUM, "passive | inline | read-file", "passive", "DAQ module mode" }, + { "variables", Parameter::PT_LIST, daqvar_list_param, nullptr, "DAQ module variables" }, - // FIXIT-L these are not exactly DAQ counts - but they are related - { CountType::SUM, "internal_blacklist", - "packets blacklisted internally due to lack of DAQ support" }, - { CountType::SUM, "internal_whitelist", - "packets whitelisted internally due to lack of DAQ support" }, - { CountType::SUM, "skipped", "packets skipped at startup" }, - { CountType::SUM, "idle", "attempts to acquire from DAQ without available packets" }, - { CountType::SUM, "rx_bytes", "total bytes received" }, - { CountType::END, nullptr, nullptr } + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; -static THREAD_LOCAL DAQStats stats; - -static const Parameter string_list_param[] = +static const Parameter path_list_param[] = { - { "str", Parameter::PT_STRING, nullptr, nullptr, "string parameter" }, + { "path", Parameter::PT_STRING, nullptr, nullptr, "directory path" }, { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; -static const Parameter instance_params[] = +static const Parameter input_list_param[] = { - { "id", Parameter::PT_INT, "0:max32", nullptr, "instance ID (required)" }, - { "input_spec", Parameter::PT_STRING, nullptr, nullptr, "input specification" }, - { "variables", Parameter::PT_LIST, string_list_param, nullptr, "DAQ variables" }, + { "input", Parameter::PT_STRING, nullptr, nullptr, "input source" }, { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; static const Parameter s_params[] = { - { "module_dirs", Parameter::PT_LIST, string_list_param, nullptr, "directories to search for DAQ modules" }, - { "input_spec", Parameter::PT_STRING, nullptr, nullptr, "input specification" }, - { "module", Parameter::PT_STRING, nullptr, nullptr, "DAQ module to use" }, - { "variables", Parameter::PT_LIST, string_list_param, nullptr, "DAQ variables" }, - { "instances", Parameter::PT_LIST, instance_params, nullptr, "DAQ instance overrides" }, - { "snaplen", Parameter::PT_INT, "0:65535", nullptr, "set snap length (same as -s)" }, - { "no_promisc", Parameter::PT_BOOL, nullptr, "false", "whether to put DAQ device into promiscuous mode" }, + { "module_dirs", Parameter::PT_LIST, path_list_param, nullptr, "directories to search for dynamic DAQ modules" }, + { "inputs", Parameter::PT_LIST, input_list_param, nullptr, "input sources" }, + { "snaplen", Parameter::PT_INT, "0:65535", "1518", "set snap length (same as -s)" }, + { "batch_size", Parameter::PT_INT, "1:", "64", "set receive batch size (same as --daq-batch-size)" }, + { "modules", Parameter::PT_LIST, daq_module_param, nullptr, "DAQ modules to use" }, { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; @@ -117,65 +86,53 @@ static const Parameter s_params[] = SFDAQModule::SFDAQModule() : Module("daq", sfdaq_help, s_params) { config = nullptr; - instance_config = nullptr; + module_config = nullptr; } - bool SFDAQModule::begin(const char* fqn, int idx, SnortConfig*) { if (!strcmp(fqn, "daq")) config = new SFDAQConfig(); - - else if (!strcmp(fqn, "daq.instances")) + else if (!strcmp(fqn, "daq.modules")) { if (idx == 0) return true; - assert(!instance_config); - instance_config = new SFDAQInstanceConfig(); - - instance_id = -1; + module_config = new SFDAQModuleConfig(); } + return true; } -bool SFDAQModule::set(const char* fqn, Value& v, SnortConfig* sc) +bool SFDAQModule::set(const char* fqn, Value& v, SnortConfig*) { if (!strcmp(fqn, "daq.module_dirs")) { config->add_module_dir(v.get_string()); } - else if (!strcmp(fqn, "daq.module")) - { - config->set_module_name(v.get_string()); - } - else if (!strcmp(fqn, "daq.input_spec")) + else if (!strcmp(fqn, "daq.inputs")) { - config->set_input_spec(v.get_string()); - } - else if (!strcmp(fqn, "daq.variables")) - { - config->set_variable(v.get_string()); + config->add_input(v.get_string()); } else if (!strcmp(fqn, "daq.snaplen")) { config->set_mru_size(v.get_uint16()); } - else if (!strcmp(fqn, "daq.no_promisc")) + else if (!strcmp(fqn, "daq.batch_size")) { - v.update_mask(sc->run_flags, RUN_FLAG__NO_PROMISCUOUS); + config->set_batch_size(v.get_long()); } - else if (!strcmp(fqn, "daq.instances.id")) + else if (!strcmp(fqn, "daq.modules.name")) { - instance_id = v.get_uint32(); + module_config->name = v.get_string(); } - else if (!strcmp(fqn, "daq.instances.input_spec")) + else if (!strcmp(fqn, "daq.modules.mode")) { - instance_config->set_input_spec(v.get_string()); + module_config->mode = (SFDAQModuleConfig::SFDAQMode) (v.get_long() + 1); } - else if (!strcmp(fqn, "daq.instances.variables")) + else if (!strcmp(fqn, "daq.modules.variables")) { - instance_config->set_variable(v.get_string()); + module_config->set_variable(v.get_string()); } return true; @@ -183,20 +140,20 @@ bool SFDAQModule::set(const char* fqn, Value& v, SnortConfig* sc) bool SFDAQModule::end(const char* fqn, int idx, SnortConfig* sc) { - if (!strcmp(fqn, "daq.instances")) + if (!strcmp(fqn, "daq.modules")) { if (idx == 0) return true; - if (instance_id < 0 or config->instances[instance_id]) + if (module_config->name.empty()) { - ParseError("%s - duplicate or no DAQ instance ID specified", fqn); - delete instance_config; - instance_config = nullptr; + ParseError("%s - No module name specified!", fqn); + delete module_config; + module_config = nullptr; return false; } - config->instances[instance_id] = instance_config; - instance_config = nullptr; + config->module_configs.push_back(module_config); + module_config = nullptr; } else if (!strcmp(fqn, "daq")) { @@ -210,6 +167,57 @@ bool SFDAQModule::end(const char* fqn, int idx, SnortConfig* sc) return true; } +/* + * Module Counters + */ + +struct DAQStats +{ + PegCount pcaps; + PegCount received; + PegCount analyzed; + PegCount dropped; + PegCount filtered; + PegCount outstanding; + PegCount injected; + PegCount verdicts[MAX_DAQ_VERDICT]; + PegCount internal_blacklist; + PegCount internal_whitelist; + PegCount skipped; + PegCount idle; + PegCount rx_bytes; +}; + +const PegInfo daq_names[] = +{ + { CountType::MAX, "pcaps", "total files and interfaces processed" }, + { CountType::SUM, "received", "total packets received from DAQ" }, + { CountType::SUM, "analyzed", "total packets analyzed from DAQ" }, + { CountType::SUM, "dropped", "packets dropped" }, + { CountType::SUM, "filtered", "packets filtered out" }, + { CountType::SUM, "outstanding", "packets unprocessed" }, + { CountType::SUM, "injected", "active responses or replacements" }, + { CountType::SUM, "allow", "total allow verdicts" }, + { CountType::SUM, "block", "total block verdicts" }, + { CountType::SUM, "replace", "total replace verdicts" }, + { CountType::SUM, "whitelist", "total whitelist verdicts" }, + { CountType::SUM, "blacklist", "total blacklist verdicts" }, + { CountType::SUM, "ignore", "total ignore verdicts" }, + { CountType::SUM, "retry", "total retry verdicts" }, + + // FIXIT-L these are not exactly DAQ counts - but they are related + { CountType::SUM, "internal_blacklist", + "packets blacklisted internally due to lack of DAQ support" }, + { CountType::SUM, "internal_whitelist", + "packets whitelisted internally due to lack of DAQ support" }, + { CountType::SUM, "skipped", "packets skipped at startup" }, + { CountType::SUM, "idle", "attempts to acquire from DAQ without available packets" }, + { CountType::SUM, "rx_bytes", "total bytes received" }, + { CountType::END, nullptr, nullptr } +}; + +static THREAD_LOCAL DAQStats stats; + const PegInfo* SFDAQModule::get_pegs() const { return daq_names; @@ -239,7 +247,6 @@ static DAQ_Stats_t operator-(const DAQ_Stats_t& left, const DAQ_Stats_t& right) void SFDAQModule::prep_counts() { static THREAD_LOCAL DAQ_Stats_t sfdaq_stats; - static THREAD_LOCAL PegCount last_skipped = 0; static THREAD_LOCAL bool did_init = false; if ( !did_init ) @@ -273,12 +280,11 @@ void SFDAQModule::prep_counts() stats.internal_blacklist = aux_counts.internal_blacklist; stats.internal_whitelist = aux_counts.internal_whitelist; - stats.skipped = SnortConfig::get_conf()->pkt_skip - last_skipped; + stats.skipped = aux_counts.skipped; stats.idle = aux_counts.idle; stats.rx_bytes = aux_counts.rx_bytes; memset(&aux_counts, 0, sizeof(AuxCount)); - last_skipped = stats.skipped; sfdaq_stats = new_sfdaq_stats; for ( unsigned i = 0; i < MAX_DAQ_VERDICT; i++ ) diff --git a/src/packet_io/sfdaq_module.h b/src/packet_io/sfdaq_module.h index c126c3c4e..b855a8b0f 100644 --- a/src/packet_io/sfdaq_module.h +++ b/src/packet_io/sfdaq_module.h @@ -28,7 +28,7 @@ namespace snort struct SnortConfig; } struct SFDAQConfig; -struct SFDAQInstanceConfig; +struct SFDAQModuleConfig; class SFDAQModule : public snort::Module { @@ -51,8 +51,7 @@ public: private: SFDAQConfig* config; - SFDAQInstanceConfig* instance_config; - int instance_id; + SFDAQModuleConfig* module_config; }; #endif diff --git a/src/packet_io/sfdaq_static_modules.h.in b/src/packet_io/sfdaq_static_modules.h.in new file mode 100644 index 000000000..b1d11dfd9 --- /dev/null +++ b/src/packet_io/sfdaq_static_modules.h.in @@ -0,0 +1,11 @@ + +#include + +@DAQ_STATIC_MODULE_EXTERNS@ + +static DAQ_Module_h static_daq_modules[] = +{ +@DAQ_STATIC_MODULE_DATA_ARRAY@ + nullptr +}; + diff --git a/src/packet_io/test/sfdaq_module_test.cc b/src/packet_io/test/sfdaq_module_test.cc index e60f2057d..b846a06b8 100644 --- a/src/packet_io/test/sfdaq_module_test.cc +++ b/src/packet_io/test/sfdaq_module_test.cc @@ -33,135 +33,152 @@ using namespace snort; -TEST_CASE("Kitchen Sink", "[SFDAQModule]") + +TEST_CASE("parse sfdaq config", "[SFDAQModule]") { - SFDAQModule sfdm; SnortConfig sc; + SFDAQModule sfdm; /* Generate the configuration */ sfdm.begin("daq", 0, &sc); Value module_dir1("/test/dir/1"); - Value module_dir2("/test/dir/2"); CHECK(sfdm.set("daq.module_dirs", module_dir1, &sc)); + + Value module_dir2("/test/dir/2"); CHECK(sfdm.set("daq.module_dirs", module_dir2, &sc)); - Value module_name("test_module"); - CHECK(sfdm.set("daq.module", module_name, &sc)); + Value input1("test_input1"); + CHECK(sfdm.set("daq.inputs", input1, &sc)); - Value input_spec("test_input"); - CHECK(sfdm.set("daq.input_spec", input_spec, &sc)); + Value input2("test_input2"); + CHECK(sfdm.set("daq.inputs", input2, &sc)); - Value var1("foo=bar"); - Value var2("debug"); - Value var3("hello=world"); - CHECK(sfdm.set("daq.variables", var1, &sc)); - CHECK(sfdm.set("daq.variables", var2, &sc)); - CHECK(sfdm.set("daq.variables", var3, &sc)); + Value input3("test_input3"); + CHECK(sfdm.set("daq.inputs", input3, &sc)); Value snaplen(static_cast(6666)); CHECK(sfdm.set("daq.snaplen", snaplen, &sc)); - Value no_promisc(true); - CHECK(sfdm.set("daq.no_promisc", no_promisc, &sc)); + Value batch_size(static_cast(10)); + CHECK(sfdm.set("daq.batch_size", batch_size, &sc)); - CHECK(sfdm.begin("daq.instances", 0, &sc)); - CHECK(sfdm.begin("daq.instances", 1, &sc)); + CHECK(sfdm.begin("daq.modules", 0, &sc)); - CHECK_FALSE(sfdm.end("daq.instances", 1, &sc)); - CHECK(sfdm.begin("daq.instances", 2, &sc)); + SECTION("empty module config") + { + // Empty module table entry should fail + CHECK(sfdm.begin("daq.modules", 1, &sc)); + CHECK_FALSE(sfdm.end("daq.modules", 1, &sc)); + } + + CHECK(sfdm.begin("daq.modules", 2, &sc)); + + Value module_name("dump"); + CHECK(sfdm.set("daq.modules.name", module_name, &sc)); - Value instance_id(static_cast(5)); - CHECK(sfdm.set("daq.instances.id", instance_id, &sc)); + Value mode_val("passive"); + Parameter mode_param = { "mode", Parameter::PT_ENUM, "passive | inline | read-file", "passive", "DAQ module mode" }; + mode_val.set(&mode_param); + CHECK(sfdm.set("daq.modules.mode", mode_val, &sc)); - Value instance_input_spec("instance_5_input"); - CHECK(sfdm.set("daq.instances.input_spec", instance_input_spec, &sc)); + Value dump_var1("dump_var1=foo"); + CHECK(sfdm.set("daq.modules.variables", dump_var1, &sc)); - Value instance_var1("instance5_var1=foo"); - Value instance_var2("instance5_var2"); - CHECK(sfdm.set("daq.instances.variables", instance_var1, &sc)); - CHECK(sfdm.set("daq.instances.variables", instance_var2, &sc)); + Value dump_var2("dump_var2"); + CHECK(sfdm.set("daq.modules.variables", dump_var2, &sc)); - CHECK(sfdm.end("daq.instances", 2, &sc)); - CHECK(sfdm.end("daq.instances", 0, &sc)); + CHECK(sfdm.end("daq.modules", 2, &sc)); + CHECK(sfdm.end("daq.modules", 0, &sc)); CHECK(sfdm.end("daq", 0, &sc)); - /* Validate the configuration */ - SFDAQConfig *cfg = sc.daq_config; + SECTION("validate sfdaq config") + { + /* Validate the configuration */ + SFDAQConfig* cfg = sc.daq_config; + REQUIRE((cfg->module_dirs.size() == 2)); + CHECK(cfg->module_dirs[0] == module_dir1.get_string()); + CHECK(cfg->module_dirs[1] == module_dir2.get_string()); + + REQUIRE((cfg->inputs.size() == 3)); + CHECK(cfg->inputs[0] == input1.get_string()); + CHECK(cfg->inputs[1] == input2.get_string()); + CHECK(cfg->inputs[2] == input3.get_string()); + + CHECK((cfg->mru_size == 6666)); + CHECK((cfg->batch_size == 10)); + + REQUIRE(cfg->module_configs.size() == 1); + for (auto it : cfg->module_configs) + { + SFDAQModuleConfig* mcfg = it; + CHECK((mcfg->name == module_name.get_string())); + CHECK((mcfg->mode == SFDAQModuleConfig::SFDAQ_MODE_PASSIVE)); + REQUIRE((mcfg->variables.size() == 2)); + CHECK(mcfg->variables[0].first == "dump_var1"); + CHECK(mcfg->variables[0].second == "foo"); + CHECK(mcfg->variables[1].first == dump_var2.get_string()); + CHECK(mcfg->variables[1].second.empty()); + } + } - REQUIRE((cfg->module_dirs.size() == 2)); - CHECK(cfg->module_dirs[0] == module_dir1.get_string()); - CHECK(cfg->module_dirs[1] == module_dir2.get_string()); + SECTION("sfdaq command line config and overlay verification") + { + /* Secondary config to overlay from, for example, the command line */ + SFDAQConfig overlay_cfg; - CHECK(cfg->module_name == module_name.get_string()); + overlay_cfg.add_module_dir("cli_module_dir"); + overlay_cfg.add_input("cli_input"); + overlay_cfg.set_mru_size(3333); + overlay_cfg.set_batch_size(12); - CHECK(cfg->input_spec == input_spec.get_string()); + SFDAQModuleConfig* cli_module_cfg = overlay_cfg.add_module_config("cli_module_name"); - REQUIRE((cfg->variables.size() == 3)); - CHECK(cfg->variables[0].first == "foo"); - CHECK(cfg->variables[0].second == "bar"); - CHECK(cfg->variables[1].first == "debug"); - CHECK(cfg->variables[1].second.empty()); - CHECK(cfg->variables[2].first == "hello"); - CHECK(cfg->variables[2].second == "world"); + cli_module_cfg->set_variable("cli_module_variable=abc"); + cli_module_cfg->mode = SFDAQModuleConfig::SFDAQ_MODE_READ_FILE; - CHECK((cfg->mru_size == 6666)); + SFDAQModuleConfig* cli_dump_cfg = overlay_cfg.add_module_config("dump"); - REQUIRE(cfg->instances.size() == 1); - for (auto it : cfg->instances) - { - CHECK((it.first == 5)); - SFDAQInstanceConfig* icfg = it.second; - CHECK(icfg->input_spec == instance_input_spec.get_string()); - REQUIRE((icfg->variables.size() == 2)); - CHECK(icfg->variables[0].first == "instance5_var1"); - CHECK(icfg->variables[0].second == "foo"); - CHECK(icfg->variables[1].first == instance_var2.get_string()); - CHECK(icfg->variables[1].second.empty()); - } + cli_dump_cfg->mode = SFDAQModuleConfig::SFDAQ_MODE_INLINE; + cli_dump_cfg->set_variable("dump_var3"); + cli_dump_cfg->set_variable("dump_var4=bar"); + cli_dump_cfg->set_variable("dump_var5=foo"); - /* Secondary config to overlay from, for example, the command line */ - SnortConfig sc2; - - sc2.daq_config->add_module_dir("cli_module_dir"); - sc2.daq_config->set_module_name("cli_module_name"); - sc2.daq_config->set_input_spec(nullptr); - sc2.daq_config->set_input_spec("cli_input_spec"); - sc2.daq_config->set_variable("cli_global_variable=abc"); - sc2.daq_config->set_mru_size(3333); - sc2.daq_config->set_input_spec(nullptr, 2); - sc2.daq_config->set_input_spec("cli_instance_2_input", 2); - sc2.daq_config->set_input_spec("cli_instance_5_input", 5); - sc2.daq_config->set_variable("cli_instance_5_var1=def", 5); - - cfg->overlay(sc2.daq_config); - - REQUIRE(cfg->module_dirs.size() == 1); - CHECK(cfg->module_dirs[0] == "cli_module_dir"); - CHECK(cfg->module_name == "cli_module_name"); - CHECK(cfg->input_spec == "cli_input_spec"); - REQUIRE(cfg->variables.size() == 1); - CHECK(cfg->variables[0].first == "cli_global_variable"); - CHECK(cfg->variables[0].second == "abc"); - CHECK((cfg->mru_size == 3333)); - REQUIRE((cfg->instances.size() == 2)); - for (auto it : cfg->instances) - { - CHECK((it.first == 2 || it.first == 5)); - if (it.first == 2) - { - SFDAQInstanceConfig* icfg = it.second; - CHECK(icfg->input_spec == "cli_instance_2_input"); - CHECK(icfg->variables.empty()); - } - else if (it.first == 5) + SFDAQConfig* cfg = sc.daq_config; + cfg->overlay(&overlay_cfg); + + REQUIRE(cfg->module_dirs.size() == 1); + CHECK(cfg->module_dirs[0] == "cli_module_dir"); + + REQUIRE((cfg->inputs.size() == 1)); + CHECK(cfg->inputs[0] == "cli_input"); + + CHECK((cfg->mru_size == 3333)); + CHECK((cfg->batch_size == 12)); + + REQUIRE(cfg->module_configs.size() == 2); + for (auto it : cfg->module_configs) { - SFDAQInstanceConfig* icfg = it.second; - CHECK(icfg->input_spec == "cli_instance_5_input"); - REQUIRE(icfg->variables.size() == 1); - CHECK(icfg->variables[0].first == "cli_instance_5_var1"); - CHECK(icfg->variables[0].second == "def"); + SFDAQModuleConfig* mcfg = it; + CHECK((mcfg->name == "cli_module_name" or mcfg->name == "dump")); + if (mcfg->name == "cli_module_name") + { + CHECK(mcfg->mode == SFDAQModuleConfig::SFDAQ_MODE_READ_FILE); + REQUIRE((mcfg->variables.size() == 1)); + CHECK(mcfg->variables[0].first == "cli_module_variable"); + CHECK(mcfg->variables[0].second == "abc"); + } + else if (mcfg->name == "dump") + { + CHECK(mcfg->mode == SFDAQModuleConfig::SFDAQ_MODE_INLINE); + REQUIRE((mcfg->variables.size() == 3)); + CHECK(mcfg->variables[0].first == "dump_var3"); + CHECK(mcfg->variables[0].second.empty()); + CHECK(mcfg->variables[1].first == "dump_var4"); + CHECK(mcfg->variables[1].second == "bar"); + CHECK(mcfg->variables[2].first == "dump_var5"); + CHECK(mcfg->variables[2].second == "foo"); + } } } } - diff --git a/src/parser/config_file.cc b/src/parser/config_file.cc index 32fedd8ba..2cb6889da 100644 --- a/src/parser/config_file.cc +++ b/src/parser/config_file.cc @@ -28,7 +28,7 @@ #include "detection/detect.h" #include "detection/detection_engine.h" #include "log/messages.h" -#include "main/snort.h" +#include "main/analyzer.h" #include "main/policy.h" #include "mstring.h" @@ -44,6 +44,8 @@ #define CHECKSUM_MODE_OPT__ICMP "icmp" #define CHECKSUM_MODE_OPT__NO_ICMP "noicmp" +using namespace snort; + static std::string lua_conf; static std::string snort_conf_dir; @@ -67,7 +69,7 @@ static int GetChecksumFlags(const char* args) if (args == nullptr) return CHECKSUM_FLAG__ALL; - toks = snort::mSplit(args, " \t", 10, &num_toks, 0); + toks = mSplit(args, " \t", 10, &num_toks, 0); for (i = 0; i < num_toks; i++) { if (strcasecmp(toks[i], CHECKSUM_MODE_OPT__ALL) == 0) @@ -132,7 +134,7 @@ static int GetChecksumFlags(const char* args) } else { - snort::ParseError("unknown command line checksum option: %s.", toks[i]); + ParseError("unknown command line checksum option: %s.", toks[i]); return ret_flags; } } @@ -160,19 +162,19 @@ static int GetChecksumFlags(const char* args) ret_flags = negative_flags; } - snort::mSplitFree(&toks, num_toks); + mSplitFree(&toks, num_toks); return ret_flags; } void ConfigChecksumDrop(const char* args) { - NetworkPolicy* policy = snort::get_network_policy(); + NetworkPolicy* policy = get_network_policy(); policy->checksum_drop = GetChecksumFlags(args); } void ConfigChecksumMode(const char* args) { - NetworkPolicy* policy = snort::get_network_policy(); + NetworkPolicy* policy = get_network_policy(); policy->checksum_eval = GetChecksumFlags(args); } @@ -180,7 +182,7 @@ void config_conf(const char* val) { lua_conf = val; SetSnortConfDir(lua_conf.c_str()); - snort::Snort::set_main_hook(snort::DetectionEngine::inspect); + Analyzer::set_main_hook(DetectionEngine::inspect); } void SetSnortConfDir(const char* file) diff --git a/src/piglet_plugins/pp_codec_iface.cc b/src/piglet_plugins/pp_codec_iface.cc index 2e475d403..344c94edd 100644 --- a/src/piglet_plugins/pp_codec_iface.cc +++ b/src/piglet_plugins/pp_codec_iface.cc @@ -112,18 +112,14 @@ static const luaL_Reg methods[] = if ( RawBufferIface.is(L, 2) ) { - RawData rd(&daq, get_data(RawBufferIface.get(L, 2))); + RawData rd(nullptr, &daq, get_data(RawBufferIface.get(L, 2)), get_data_length(RawBufferIface.get(L, 2))); result = self.decode(rd, cd, dd); } else { size_t len = 0; - RawData rd( - &daq, - reinterpret_cast( - luaL_checklstring(L, 2, &len) - ) - ); + const uint8_t* data = reinterpret_cast(luaL_checklstring(L, 2, &len)); + RawData rd(nullptr, &daq, data, len); result = self.decode(rd, cd, dd); } diff --git a/src/piglet_plugins/pp_daq_pkthdr_iface.cc b/src/piglet_plugins/pp_daq_pkthdr_iface.cc index 0cbaecbfa..669fcbc9b 100644 --- a/src/piglet_plugins/pp_daq_pkthdr_iface.cc +++ b/src/piglet_plugins/pp_daq_pkthdr_iface.cc @@ -29,11 +29,10 @@ #include "lua/lua_arg.h" -static void set_fields(lua_State* L, int tindex, struct _daq_pkthdr& self) +static void set_fields(lua_State* L, int tindex, DAQ_PktHdr_t& self) { Lua::Table table(L, tindex); - table.get_field("caplen", self.caplen); table.get_field("pktlen", self.pktlen); table.get_field("ingress_index", self.ingress_index); table.get_field("egress_index", self.egress_index); @@ -47,11 +46,10 @@ static void set_fields(lua_State* L, int tindex, struct _daq_pkthdr& self) // FIXIT-L do we want to be able to set the priv_ptr field? } -static void get_fields(lua_State* L, int tindex, struct _daq_pkthdr& self) +static void get_fields(lua_State* L, int tindex, DAQ_PktHdr_t& self) { Lua::Table table(L, tindex); - table.set_field("caplen", self.caplen); table.set_field("pktlen", self.pktlen); table.set_field("ingress_index", self.ingress_index); table.set_field("egress_index", self.egress_index); @@ -107,7 +105,7 @@ static const luaL_Reg metamethods[] = { nullptr, nullptr } }; -const struct Lua::TypeInterface<_daq_pkthdr> DAQHeaderIface = +const struct Lua::TypeInterface DAQHeaderIface = { "DAQHeader", methods, diff --git a/src/piglet_plugins/pp_daq_pkthdr_iface.h b/src/piglet_plugins/pp_daq_pkthdr_iface.h index b363b4561..8e5a28829 100644 --- a/src/piglet_plugins/pp_daq_pkthdr_iface.h +++ b/src/piglet_plugins/pp_daq_pkthdr_iface.h @@ -22,8 +22,8 @@ #include "lua/lua_iface.h" -struct _daq_pkthdr; +struct _daq_pkt_hdr; -extern const struct Lua::TypeInterface<_daq_pkthdr> DAQHeaderIface; +extern const struct Lua::TypeInterface<_daq_pkt_hdr> DAQHeaderIface; #endif diff --git a/src/piglet_plugins/pp_raw_buffer_iface.h b/src/piglet_plugins/pp_raw_buffer_iface.h index 434a17345..91b2f5c4d 100644 --- a/src/piglet_plugins/pp_raw_buffer_iface.h +++ b/src/piglet_plugins/pp_raw_buffer_iface.h @@ -32,6 +32,9 @@ inline const uint8_t* get_data(RawBuffer& rb) inline uint8_t* get_mutable_data(RawBuffer& rb) { return const_cast(get_data(rb)); } +inline size_t get_data_length(RawBuffer& rb) +{ return rb.size(); } + extern const struct Lua::TypeInterface RawBufferIface; #endif diff --git a/src/protocols/packet.h b/src/protocols/packet.h index bddf5625d..4e25fc1d4 100644 --- a/src/protocols/packet.h +++ b/src/protocols/packet.h @@ -36,6 +36,7 @@ class Flow; class IpsAction; class IpsContext; class Obfuscator; +class SFDAQInstance; /* packet status flags */ #define PKT_REBUILT_FRAG 0x00000001 /* is a rebuilt fragment */ @@ -77,7 +78,8 @@ class Obfuscator; #define PKT_FILE_EVENT_SET 0x00400000 #define PKT_IGNORE 0x00800000 /* this packet should be ignored, based on port */ #define PKT_RETRANSMIT 0x01000000 // packet is a re-transmitted pkt. -#define PKT_UNUSED_FLAGS 0xfe000000 +#define PKT_RETRY 0x02000000 /* this packet is being re-evaluated from the internal retry queue */ +#define PKT_UNUSED_FLAGS 0xfc000000 #define PKT_TS_OFFLOADED 0x01 @@ -130,16 +132,21 @@ struct SO_PUBLIC Packet // FIXIT-M Consider moving ip_proto_next below `pkth`. IpProtocol ip_proto_next; /* the protocol ID after IP and all IP6 extension */ bool disable_inspect; - // nothing after this point is zeroed ... + // nothing after this point is zeroed by reset() ... - // Everything beyond this point is set by PacketManager::decode() - IpsContext* context; // set by control + IpsContext* context; Active* active; Active* active_inst; IpsAction** action; IpsAction* action_inst; - const DAQ_PktHdr_t* pkth; // packet meta data - const uint8_t* pkt; // raw packet data + + DAQ_Msg_h daq_msg; // DAQ message this packet came from + SFDAQInstance* daq_instance; // DAQ instance the message came from + + // Everything beyond this point is set by PacketManager::decode() + const DAQ_PktHdr_t* pkth; // packet meta data + const uint8_t* pkt; // raw packet data + uint32_t pktlen; // raw packet data length // These are both set before PacketManager::decode() returns const uint8_t* data; /* packet payload pointer */ @@ -272,6 +279,9 @@ struct SO_PUBLIC Packet bool is_rebuilt() const { return (packet_flags & (PKT_REBUILT_STREAM|PKT_REBUILT_FRAG)) != 0; } + bool is_retry() const + { return (packet_flags & PKT_RETRY) != 0; } + bool is_offloaded() const { return (ts_packet_flags & PKT_TS_OFFLOADED) != 0; } diff --git a/src/protocols/packet_manager.cc b/src/protocols/packet_manager.cc index e424245f1..adb545526 100644 --- a/src/protocols/packet_manager.cc +++ b/src/protocols/packet_manager.cc @@ -123,18 +123,11 @@ static_assert(CODEC_ENCAP_LAYER == (CODEC_UNSURE_ENCAP | CODEC_SAVE_LAYER), "If this is an encapsulated layer, you must also set UNSURE_ENCAP" " and SAVE_LAYER"); -RawData::RawData(const DAQ_PktHdr_t* h, const uint8_t* p) -{ - pkth = h; - data = p; - len = h->caplen; -} - //------------------------------------------------------------------------- // Encode/Decode functions //------------------------------------------------------------------------- void PacketManager::decode( - Packet* p, const DAQ_PktHdr_t* pkthdr, const uint8_t* pkt, bool cooked) + Packet* p, const DAQ_PktHdr_t* pkthdr, const uint8_t* pkt, uint32_t pktlen, bool cooked, bool retry) { Profile profile(decodePerfStats); @@ -143,7 +136,7 @@ void PacketManager::decode( ProtocolIndex mapped_prot = CodecManager::grinder; ProtocolId prev_prot_id = CodecManager::grinder_id; - RawData raw(pkthdr, pkt); + RawData raw(p->daq_msg, pkthdr, pkt, pktlen); CodecData codec_data(ProtocolId::FINISHED_DECODE); if ( cooked ) @@ -153,6 +146,9 @@ void PacketManager::decode( p->reset(); p->pkth = pkthdr; p->pkt = pkt; + p->pktlen = pktlen; + if (retry) + p->packet_flags |= PKT_RETRY; layer::set_packet_pointer(p); s_stats[total_processed]++; @@ -668,7 +664,7 @@ int PacketManager::format_tcp( // setup pkt capture header DAQ_PktHdr_t* pkth = const_cast(c->pkth); - pkth->caplen = 0; + c->pktlen = 0; pkth->pktlen = 0; pkth->ts = p->pkth->ts; @@ -774,7 +770,7 @@ int PacketManager::encode_format( // setup pkt capture header DAQ_PktHdr_t* pkth = const_cast(c->pkth); - pkth->caplen = len; + c->pktlen = len; pkth->pktlen = len; pkth->ts = p->pkth->ts; @@ -842,7 +838,7 @@ void PacketManager::encode_update(Packet* p) if ( !(p->packet_flags & PKT_MODIFIED) || (p->packet_flags & PKT_RESIZED) ) { DAQ_PktHdr_t* pkth = const_cast(p->pkth); - pkth->caplen = len; + p->pktlen = len; pkth->pktlen = len; } } diff --git a/src/protocols/packet_manager.h b/src/protocols/packet_manager.h index 2e5a363ae..21450d8ab 100644 --- a/src/protocols/packet_manager.h +++ b/src/protocols/packet_manager.h @@ -61,7 +61,8 @@ public: static void thread_term(); // decode this packet and set all relevant packet fields. - static void decode(Packet*, const struct _daq_pkthdr*, const uint8_t*, bool cooked = false); + static void decode(Packet*, const struct _daq_pkt_hdr*, const uint8_t* pkt, + uint32_t pktlen, bool cooked = false, bool retry = false); // update the packet's checksums and length variables. Call this function // after Snort has changed any data in this packet diff --git a/src/service_inspectors/dce_rpc/dce_smb_utils.cc b/src/service_inspectors/dce_rpc/dce_smb_utils.cc index 1bb713bfb..4251fa9e7 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_utils.cc +++ b/src/service_inspectors/dce_rpc/dce_smb_utils.cc @@ -1431,7 +1431,7 @@ void DCE2_SmbInitDeletePdu() static void DCE2_SmbInjectDeletePdu(DCE2_SmbFileTracker* ftracker) { - Packet* inject_pkt = snort::Snort::get_packet(); + Packet* inject_pkt = DetectionEngine::get_current_wire_packet(); Packet* p = DetectionEngine::get_current_packet(); if ( inject_pkt->flow != p->flow ) diff --git a/src/stream/file/file_session.cc b/src/stream/file/file_session.cc index d76e19b66..c096feb7c 100644 --- a/src/stream/file/file_session.cc +++ b/src/stream/file/file_session.cc @@ -85,7 +85,7 @@ int FileSession::process(Packet* p) if (file_flows && file_flows->file_process(p, p->data, p->dsize, position(p), c->upload)) { - const char* file_name = SFDAQ::get_interface_spec(); + const char* file_name = SFDAQ::get_input_spec(); if (file_name) file_flows->set_file_name((const uint8_t*)file_name, strlen(file_name)); } diff --git a/src/stream/ip/ip_defrag.cc b/src/stream/ip/ip_defrag.cc index 1a133efc8..bda68f334 100644 --- a/src/stream/ip/ip_defrag.cc +++ b/src/stream/ip/ip_defrag.cc @@ -73,11 +73,11 @@ #include "detection/detect.h" #include "detection/detection_engine.h" #include "log/messages.h" -#include "main/snort.h" +#include "main/analyzer.h" #include "main/snort_config.h" #include "memory/memory_cap.h" #include "packet_io/active.h" -#include "packet_io/sfdaq.h" +#include "packet_io/sfdaq_config.h" #include "profiler/profiler_defs.h" #include "protocols/ipv4_options.h" #include "time/timersub.h" @@ -739,7 +739,7 @@ static void FragRebuild(FragTracker* ft, Packet* p) "Processing rebuilt packet:\n"); ip_stats.reassembles++; - ip_stats.reassembled_bytes += dpkt->pkth->caplen; + ip_stats.reassembled_bytes += dpkt->pktlen; #if defined(DEBUG_FRAG_EX) && defined(DEBUG_MSGS) /* @@ -752,7 +752,7 @@ static void FragRebuild(FragTracker* ft, Packet* p) DetectionEngine de; de.set_encode_packet(p); - snort::Snort::process_packet(dpkt, dpkt->pkth, dpkt->pkt, true); + Analyzer::get_local_analyzer()->process_rebuilt_packet(dpkt, dpkt->pkth, dpkt->pkt, dpkt->pktlen); de.set_encode_packet(nullptr); trace_log(stream_ip, "Done with rebuilt packet, marking rebuilt...\n"); @@ -946,7 +946,7 @@ void Defrag::process(Packet* p, FragTracker* ft) } ip_stats.total++; - ip_stats.fragmented_bytes += p->pkth->caplen + 4; /* 4 for the CRC */ + ip_stats.fragmented_bytes += p->pktlen + 4; /* 4 for the CRC */ DeepProfile profile(fragPerfStats); @@ -1439,7 +1439,7 @@ left_overlap_last: } } - if ((uint16_t)fragLength > SFDAQ::get_snap_len()) + if ((uint16_t)fragLength > SnortConfig::get_conf()->daq_config->get_mru_size()) { trace_logf(stream_ip, "Overly large fragment %d 0x%x 0x%x %d\n", @@ -1814,7 +1814,7 @@ int Defrag::new_tracker(Packet* p, FragTracker* ft) fragStart = p->data; /* Just to double check */ - if (!fragLength or fragLength > SFDAQ::get_snap_len()) + if (!fragLength or fragLength > SnortConfig::get_conf()->daq_config->get_mru_size()) { trace_logf(stream_ip, "Bad fragment length:%d(0x%x) off:0x%x(%d)\n", diff --git a/src/stream/tcp/segment_overlap_editor.cc b/src/stream/tcp/segment_overlap_editor.cc index de9e80998..ba410a289 100644 --- a/src/stream/tcp/segment_overlap_editor.cc +++ b/src/stream/tcp/segment_overlap_editor.cc @@ -38,13 +38,13 @@ static void set_retransmit_flag(snort::Packet* p) if ( snort::PacketTracer::is_active() ) { snort::PacketTracer::log("Packet was retransmitted and %s from the retry queue.\n", - (p->pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET) ? "is" : "is not"); + p->is_retry() ? "is" : "is not"); } // Mark the packet as being a re-transmit if it's not from the retry // queue. That way we can avoid adding re-transmitted packets to // the retry queue. - if ( !(p->pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET) ) + if ( !p->is_retry() ) p->packet_flags |= PKT_RETRANSMIT; } diff --git a/src/stream/tcp/tcp_normalizer.cc b/src/stream/tcp/tcp_normalizer.cc index 16bff360e..ade9912e7 100644 --- a/src/stream/tcp/tcp_normalizer.cc +++ b/src/stream/tcp/tcp_normalizer.cc @@ -267,7 +267,7 @@ int TcpNormalizer::validate_paws_timestamp( { if ( ( (int)( ( tsd.get_ts() - tns.peer_tracker->get_ts_last() ) + tns.paws_ts_fudge ) ) < 0 ) { - if ( tsd.get_pkt()->pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET ) + if ( tsd.get_pkt()->is_retry() ) { // Retry packets can legitimately have old timestamps // in TCP options (if a re-transmit comes in before diff --git a/src/stream/tcp/tcp_reassembler.cc b/src/stream/tcp/tcp_reassembler.cc index f97fde5e9..b59dc0624 100644 --- a/src/stream/tcp/tcp_reassembler.cc +++ b/src/stream/tcp/tcp_reassembler.cc @@ -27,7 +27,7 @@ #include "detection/detection_engine.h" #include "log/log.h" -#include "main/snort.h" +#include "main/analyzer.h" #include "memory/memory_cap.h" #include "profiler/profiler.h" #include "protocols/packet_manager.h" @@ -584,7 +584,7 @@ int TcpReassembler::_flush_to_seq( NoProfile exclude(s5TcpFlushPerfStats); - if ( !Snort::inspect(pdu) ) + if ( !Analyzer::get_local_analyzer()->inspect_rebuilt(pdu) ) last_pdu = pdu; else last_pdu = nullptr; @@ -670,7 +670,7 @@ int TcpReassembler::do_zero_byte_flush(TcpReassemblerState& trs, Packet* p, uint show_rebuilt_packet(trs, pdu); NoProfile profile_exclude(s5TcpFlushPerfStats); - Snort::inspect(pdu); + Analyzer::get_local_analyzer()->inspect_rebuilt(pdu); if ( trs.tracker->splitter ) trs.tracker->splitter->update(); @@ -822,6 +822,7 @@ static Packet* set_packet(Flow* flow, uint32_t flags, bool c2s) memset(ph, 0, sizeof(*ph)); packet_gettimeofday(&ph->ts); + p->pktlen = 0; p->data = nullptr; p->dsize = 0; diff --git a/src/stream/user/user_session.cc b/src/stream/user/user_session.cc index e510791af..82da5a5b0 100644 --- a/src/stream/user/user_session.cc +++ b/src/stream/user/user_session.cc @@ -25,7 +25,7 @@ #include "detection/detection_engine.h" #include "detection/rules.h" -#include "main/snort.h" +#include "main/analyzer.h" #include "memory/memory_cap.h" #include "profiler/profiler_defs.h" #include "protocols/packet.h" @@ -166,7 +166,7 @@ void UserTracker::detect( up->packet_flags |= (p->packet_flags & (PKT_STREAM_EST|PKT_STREAM_UNEST_UNI)); trace_logf(stream_user, "detect[%d]\n", up->dsize); - snort::Snort::inspect(up); + Analyzer::get_local_analyzer()->inspect_rebuilt(up); } int UserTracker::scan(Packet* p, uint32_t& flags) diff --git a/src/time/packet_time.cc b/src/time/packet_time.cc index 512d15bac..b6607d418 100644 --- a/src/time/packet_time.cc +++ b/src/time/packet_time.cc @@ -64,10 +64,13 @@ int64_t timersub_ms(const struct timeval* end, const struct timeval* start) void packet_time_update(const struct timeval* cur_tv) { - if ( !s_first_packet ) - s_first_packet = cur_tv->tv_sec; + if (timercmp(cur_tv, &s_recent_packet, >)) + { + if ( !s_first_packet ) + s_first_packet = cur_tv->tv_sec; - s_recent_packet = *cur_tv; + s_recent_packet = *cur_tv; + } } uint32_t packet_first_time() diff --git a/src/utils/stats.h b/src/utils/stats.h index e55f35ba0..24a66f540 100644 --- a/src/utils/stats.h +++ b/src/utils/stats.h @@ -76,6 +76,7 @@ struct AuxCount PegCount internal_whitelist; PegCount idle; PegCount rx_bytes; + PegCount skipped; }; extern ProcessCount proc_stats; diff --git a/src/utils/util.cc b/src/utils/util.cc index 1fe44e799..316533c2e 100644 --- a/src/utils/util.cc +++ b/src/utils/util.cc @@ -285,7 +285,7 @@ void CreatePidFile(pid_t pid) else { const char* error = get_error(errno); - ErrorMessage("Failed to create pid file %s, Error: %s", + ErrorMessage("Failed to create pid file %s, Error: %s\n", SnortConfig::get_conf()->pid_filename.c_str(), error); SnortConfig::get_conf()->pid_filename.clear(); } @@ -335,13 +335,6 @@ bool SetUidGid(int user_id, int group_id) if (group_id == -1 && user_id == -1) return true; - // FIXIT-L Move this check to Snort::drop_privileges() - if (!SFDAQ::unprivileged()) - { - ParseError("Cannot drop privileges - %s DAQ does not support unprivileged operation.\n", SFDAQ::get_type()); - return false; - } - if (group_id != -1) { if (setgid(group_id) < 0) diff --git a/tools/snort2lua/config_states/CMakeLists.txt b/tools/snort2lua/config_states/CMakeLists.txt index 6fa05ccde..be046f468 100644 --- a/tools/snort2lua/config_states/CMakeLists.txt +++ b/tools/snort2lua/config_states/CMakeLists.txt @@ -4,6 +4,9 @@ add_library( config_states OBJECT config_binding.cc config_checksums.cc config_classification.cc + config_daq.cc + config_daq_mode.cc + config_daq_var.cc config_decode_esp.cc config_default_rule_state.cc config_deleted.cc diff --git a/tools/snort2lua/config_states/config_daq.cc b/tools/snort2lua/config_states/config_daq.cc new file mode 100644 index 000000000..eef361faf --- /dev/null +++ b/tools/snort2lua/config_states/config_daq.cc @@ -0,0 +1,76 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// config_daq.cc author Carter Waxman + +#include +#include + +#include "conversion_state.h" +#include "helpers/converter.h" +#include "helpers/s2l_util.h" + +namespace config +{ +namespace +{ +class Daq : public ConversionState +{ +public: + Daq(Converter& c) : ConversionState(c) { } + + bool convert(std::istringstream& data_stream) override; +}; +} // namespace + +bool Daq::convert(std::istringstream& data_stream) +{ + bool retval = true; + std::string name; + + if ( data_stream >> name ) + { + table_api.open_top_level_table("daq"); + table_api.open_table("modules"); + table_api.open_table(""); + table_api.add_option("name", name); + table_api.add_diff_option_comment("config daq:", "name"); + table_api.close_table(); + table_api.close_table(); + table_api.close_table(); + } + + return retval; +} + +/************************** + ******* A P I *********** + **************************/ + +static ConversionState* ctor(Converter& c) +{ return new Daq(c); } + +static const ConvertMap daq_api = +{ + "daq", + ctor, +}; + +const ConvertMap* daq_map = &daq_api; + +} // namespace config + diff --git a/tools/snort2lua/config_states/config_daq_mode.cc b/tools/snort2lua/config_states/config_daq_mode.cc new file mode 100644 index 000000000..a674da485 --- /dev/null +++ b/tools/snort2lua/config_states/config_daq_mode.cc @@ -0,0 +1,83 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// config_daq_mode.cc author Carter Waxman + +#include +#include + +#include "conversion_state.h" +#include "helpers/converter.h" +#include "helpers/s2l_util.h" + +namespace config +{ +namespace +{ +class DaqMode : public ConversionState +{ +public: + DaqMode(Converter& c) : ConversionState(c) { } + bool convert(std::istringstream& data_stream) override; +}; +} // namespace + +bool DaqMode::convert(std::istringstream& data_stream) +{ + bool retval = true; + std::string mode; + + if ( data_stream >> mode ) + { + if ( mode == "read-file" or mode == "passive" or mode == "inline" ) + { + table_api.open_top_level_table("daq"); + table_api.open_table("modules"); + table_api.open_table(""); + table_api.add_option("mode", mode); + table_api.add_diff_option_comment("config daq_mode:", "mode"); + table_api.close_table(); + table_api.close_table(); + table_api.close_table(); + } + else + { + retval = false; + data_api.failed_conversion(data_stream, mode); + } + } + + return retval; +} + +/************************** + ******* A P I *********** + **************************/ + +static ConversionState* ctor(Converter& c) +{ return new DaqMode(c); } + +static const ConvertMap daq_mode_api = +{ + "daq_mode", + ctor, +}; + +const ConvertMap* daq_mode_map = &daq_mode_api; + +} // namespace config + diff --git a/tools/snort2lua/config_states/config_daq_var.cc b/tools/snort2lua/config_states/config_daq_var.cc new file mode 100644 index 000000000..8d2f6cdd1 --- /dev/null +++ b/tools/snort2lua/config_states/config_daq_var.cc @@ -0,0 +1,80 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// config_daq_var.cc author Carter Waxman + +#include + +#include "conversion_state.h" +#include "helpers/converter.h" +#include "helpers/s2l_util.h" + +namespace config +{ +namespace +{ +class DaqVar : public ConversionState +{ +public: + DaqVar(Converter& c) : ConversionState(c) { } + + bool convert(std::istringstream& data_stream) override; +}; +} // namespace + +bool DaqVar::convert(std::istringstream& data_stream) +{ + bool retval = true; + std::string tmp, var; + + while ( data_stream >> tmp ) + var += tmp; + + if ( !var.empty() ) + { + table_api.open_top_level_table("daq"); + table_api.open_table("modules"); + table_api.open_table(""); + table_api.open_table("variables"); + table_api.add_option(var); + table_api.close_table(); + table_api.add_diff_option_comment("config daq_var:", "variables"); + table_api.close_table(); + table_api.close_table(); + table_api.close_table(); + } + + return retval; +} + +/************************** + ******* A P I *********** + **************************/ + +static ConversionState* ctor(Converter& c) +{ return new DaqVar(c); } + +static const ConvertMap daq_var_api = +{ + "daq_var", + ctor, +}; + +const ConvertMap* daq_var_map = &daq_var_api; + +} // namespace config + diff --git a/tools/snort2lua/config_states/config_deleted.cc b/tools/snort2lua/config_states/config_deleted.cc index 974adc65d..b6b465651 100644 --- a/tools/snort2lua/config_states/config_deleted.cc +++ b/tools/snort2lua/config_states/config_deleted.cc @@ -532,19 +532,6 @@ static const ConvertMap disable_inline_init_failopen_api = const ConvertMap* disable_inline_init_failopen_map = &disable_inline_init_failopen_api; -/************************************************* - ***************** daq_mode ******************* - *************************************************/ - -static const std::string daq_mode = "daq_mode"; -static const ConvertMap daq_mode_api = -{ - daq_mode, - deleted_ctor<& daq_mode>, -}; - -const ConvertMap* daq_mode_map = &daq_mode_api; - /************************************************* ************* decode_data_link **************** *************************************************/ @@ -586,4 +573,17 @@ static const ConvertMap sidechannel_api = const ConvertMap* sidechannel_map = &sidechannel_api; +/************************************************* + ***************** no_promisc ******************* + *************************************************/ + +static const std::string no_promisc = "no_promisc"; +static const ConvertMap no_promisc_api = +{ + no_promisc, + deleted_ctor<& no_promisc>, +}; + +const ConvertMap* no_promisc_map = &no_promisc_api; + } // namespace config diff --git a/tools/snort2lua/config_states/config_no_option.cc b/tools/snort2lua/config_states/config_no_option.cc index c15f7bc97..5722ffecd 100644 --- a/tools/snort2lua/config_states/config_no_option.cc +++ b/tools/snort2lua/config_states/config_no_option.cc @@ -302,19 +302,6 @@ static const ConvertMap nopcre_api = const ConvertMap* nopcre_map = &nopcre_api; -/************************************************* - ****************** no_promisc ***************** - *************************************************/ - -static const std::string no_promisc = "no_promisc"; -static const ConvertMap no_promisc_api = -{ - no_promisc, - config_true_no_opt_ctor<& no_promisc, & daq> -}; - -const ConvertMap* no_promisc_map = &no_promisc_api; - /************************************************* ****************** obfuscate ****************** *************************************************/ diff --git a/tools/snort2lua/config_states/config_one_string_option.cc b/tools/snort2lua/config_states/config_one_string_option.cc index c4d0ce98a..8f5c4c1bc 100644 --- a/tools/snort2lua/config_states/config_one_string_option.cc +++ b/tools/snort2lua/config_states/config_one_string_option.cc @@ -119,7 +119,6 @@ static ConversionState* config_string_ctor(Converter& c) *************************************************/ static const std::string alerts = "alerts"; -static const std::string daq = "daq"; static const std::string mode = "mode"; static const std::string packets = "packets"; static const std::string process = "process"; @@ -152,23 +151,11 @@ static const ConvertMap chroot_api = const ConvertMap* chroot_map = &chroot_api; -/************************************************* - ********************* daq ********************* - *************************************************/ - -static const std::string module = "module"; -static const ConvertMap daq_api = -{ - daq, - config_string_ctor<& daq, & daq, & module>, -}; - -const ConvertMap* daq_map = &daq_api; - /************************************************* ******************* daq_dir ******************* *************************************************/ +static const std::string daq = "daq"; static const std::string daq_dir = "daq_dir"; static const std::string module_dirs = "module_dirs"; static const ConvertMap daq_dir_api = @@ -179,20 +166,6 @@ static const ConvertMap daq_dir_api = const ConvertMap* daq_dir_map = &daq_dir_api; -/************************************************* - ******************* daq_var ******************* - *************************************************/ - -static const std::string daq_var = "daq_var"; -static const std::string variables = "variables"; -static const ConvertMap daq_var_api = -{ - daq_var, - config_string_ctor<& daq_var, & daq, & variables, true>, -}; - -const ConvertMap* daq_var_map = &daq_var_api; - /************************************************* ******************* logdir ******************** *************************************************/ diff --git a/tools/snort2lua/data/data_types/dt_table.cc b/tools/snort2lua/data/data_types/dt_table.cc index bb25e76fb..327cc87a0 100644 --- a/tools/snort2lua/data/data_types/dt_table.cc +++ b/tools/snort2lua/data/data_types/dt_table.cc @@ -25,9 +25,6 @@ static inline Table* find_table(std::vector vec, const std::string& name) { - if (name.empty()) - return nullptr; - for ( auto* t : vec) if (name == t->get_name()) return t;