You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/tm-threads.c

2200 lines
60 KiB
C

/* Copyright (C) 2007-2012 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* 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
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Victor Julien <victor@inliniac.net>
* \author Anoop Saldanha <anoopsaldanha@gmail.com>
* \author Eric Leblond <eric@regit.org>
*
* Thread management functions.
*/
#include "suricata-common.h"
#include "suricata.h"
#include "stream.h"
#include "runmodes.h"
#include "threadvars.h"
#include "tm-queues.h"
#include "tm-queuehandlers.h"
#include "tm-threads.h"
#include "tmqh-packetpool.h"
#include "threads.h"
#include "util-debug.h"
#include "util-privs.h"
#include "util-cpu.h"
#include "util-optimize.h"
Add per packet profiling. Per packet profiling uses tick based accounting. It has 2 outputs, a summary and a csv file that contains per packet stats. Stats per packet include: 1) total ticks spent 2) ticks spent per individual thread module 3) "threading overhead" which is simply calculated by subtracting (2) of (1). A number of changes were made to integrate the new code in a clean way: a number of generic enums are now placed in tm-threads-common.h so we can include them from any part of the engine. Code depends on --enable-profiling just like the rule profiling code. New yaml parameters: profiling: # packet profiling packets: # Profiling can be disabled here, but it will still have a # performance impact if compiled in. enabled: yes filename: packet_stats.log append: yes # per packet csv output csv: # Output can be disabled here, but it will still have a # performance impact if compiled in. enabled: no filename: packet_stats.csv Example output of summary stats: IP ver Proto cnt min max avg ------ ----- ------ ------ ---------- ------- IPv4 6 19436 11448 5404365 32993 IPv4 256 4 11511 49968 30575 Per Thread module stats: Thread Module IP ver Proto cnt min max avg ------------------------ ------ ----- ------ ------ ---------- ------- TMM_DECODEPCAPFILE IPv4 6 19434 1242 47889 1770 TMM_DETECT IPv4 6 19436 1107 137241 1504 TMM_ALERTFASTLOG IPv4 6 19436 90 1323 155 TMM_ALERTUNIFIED2ALERT IPv4 6 19436 108 1359 138 TMM_ALERTDEBUGLOG IPv4 6 19436 90 1134 154 TMM_LOGHTTPLOG IPv4 6 19436 414 5392089 7944 TMM_STREAMTCP IPv4 6 19434 828 1299159 19438 The proto 256 is a counter for handling of pseudo/tunnel packets. Example output of csv: pcap_cnt,ipver,ipproto,total,TMM_DECODENFQ,TMM_VERDICTNFQ,TMM_RECEIVENFQ,TMM_RECEIVEPCAP,TMM_RECEIVEPCAPFILE,TMM_DECODEPCAP,TMM_DECODEPCAPFILE,TMM_RECEIVEPFRING,TMM_DECODEPFRING,TMM_DETECT,TMM_ALERTFASTLOG,TMM_ALERTFASTLOG4,TMM_ALERTFASTLOG6,TMM_ALERTUNIFIEDLOG,TMM_ALERTUNIFIEDALERT,TMM_ALERTUNIFIED2ALERT,TMM_ALERTPRELUDE,TMM_ALERTDEBUGLOG,TMM_ALERTSYSLOG,TMM_LOGDROPLOG,TMM_ALERTSYSLOG4,TMM_ALERTSYSLOG6,TMM_RESPONDREJECT,TMM_LOGHTTPLOG,TMM_LOGHTTPLOG4,TMM_LOGHTTPLOG6,TMM_PCAPLOG,TMM_STREAMTCP,TMM_DECODEIPFW,TMM_VERDICTIPFW,TMM_RECEIVEIPFW,TMM_RECEIVEERFFILE,TMM_DECODEERFFILE,TMM_RECEIVEERFDAG,TMM_DECODEERFDAG,threading 1,4,6,172008,0,0,0,0,0,0,47889,0,0,48582,1323,0,0,0,0,1359,0,1134,0,0,0,0,0,8028,0,0,0,49356,0,0,0,0,0,0,0,14337 First line of the file contains labels. 2 example gnuplot scripts added to plot the data.
14 years ago
#include "util-profiling.h"
#include "util-signal.h"
#include "queue.h"
#ifdef PROFILE_LOCKING
__thread uint64_t mutex_lock_contention;
__thread uint64_t mutex_lock_wait_ticks;
__thread uint64_t mutex_lock_cnt;
__thread uint64_t spin_lock_contention;
__thread uint64_t spin_lock_wait_ticks;
__thread uint64_t spin_lock_cnt;
__thread uint64_t rww_lock_contention;
__thread uint64_t rww_lock_wait_ticks;
__thread uint64_t rww_lock_cnt;
__thread uint64_t rwr_lock_contention;
__thread uint64_t rwr_lock_wait_ticks;
__thread uint64_t rwr_lock_cnt;
#endif
#ifdef OS_FREEBSD
#include <sched.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/cpuset.h>
#include <sys/thr.h>
#define cpu_set_t cpuset_t
#endif /* OS_FREEBSD */
/* prototypes */
static int SetCPUAffinity(uint16_t cpu);
/* root of the threadvars list */
ThreadVars *tv_root[TVT_MAX] = { NULL };
/* lock to protect tv_root */
SCMutex tv_root_lock = PTHREAD_MUTEX_INITIALIZER;
/* Action On Failure(AOF). Determines how the engine should behave when a
* thread encounters a failure. Defaults to restart the failed thread */
uint8_t tv_aof = THV_RESTART_THREAD;
/**
* \brief Check if a thread flag is set.
*
* \retval 1 flag is set.
* \retval 0 flag is not set.
*/
int TmThreadsCheckFlag(ThreadVars *tv, uint16_t flag)
{
return (SC_ATOMIC_GET(tv->flags) & flag) ? 1 : 0;
}
/**
* \brief Set a thread flag.
*/
void TmThreadsSetFlag(ThreadVars *tv, uint16_t flag)
{
SC_ATOMIC_OR(tv->flags, flag);
}
/**
* \brief Unset a thread flag.
*/
void TmThreadsUnsetFlag(ThreadVars *tv, uint16_t flag)
{
SC_ATOMIC_AND(tv->flags, ~flag);
}
/**
* \brief Function to use as dummy stack function
*
* \retval TM_ECODE_OK
*/
TmEcode TmDummyFunc(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
{
return TM_ECODE_OK;
}
17 years ago
/* 1 slot functions */
void *TmThreadsSlot1NoIn(void *td)
{
/* block usr2. usr2 to be handled by the main thread only */
UtilSignalBlock(SIGUSR2);
ThreadVars *tv = (ThreadVars *)td;
TmSlot *s = (TmSlot *)tv->tm_slots;
char run = 1;
TmEcode r = TM_ECODE_OK;
/* Set the thread name */
if (SCSetThreadName(tv->name) < 0) {
SCLogWarning(SC_ERR_THREAD_INIT, "Unable to set thread name");
}
if (tv->thread_setup_flags != 0)
TmThreadSetupOptions(tv);
/* Drop the capabilities for this thread */
SCDropCaps(tv);
if (s->SlotThreadInit != NULL) {
void *slot_data = NULL;
r = s->SlotThreadInit(tv, s->slot_initdata, &slot_data);
if (r != TM_ECODE_OK) {
EngineKill();
TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);
pthread_exit((void *) -1);
return NULL;
}
(void)SC_ATOMIC_SET(s->slot_data, slot_data);
}
memset(&s->slot_pre_pq, 0, sizeof(PacketQueue));
memset(&s->slot_post_pq, 0, sizeof(PacketQueue));
TmThreadsSetFlag(tv, THV_INIT_DONE);
while (run) {
if (TmThreadsCheckFlag(tv, THV_PAUSE)) {
TmThreadsSetFlag(tv, THV_PAUSED);
TmThreadTestThreadUnPaused(tv);
TmThreadsUnsetFlag(tv, THV_PAUSED);
}
TmSlotFunc SlotFunc = SC_ATOMIC_GET(s->SlotFunc);
r = SlotFunc(tv, NULL, SC_ATOMIC_GET(s->slot_data), &s->slot_pre_pq, &s->slot_post_pq);
/* handle error */
if (r == TM_ECODE_FAILED) {
TmqhReleasePacketsToPacketPool(&s->slot_pre_pq);
SCMutexLock(&s->slot_post_pq.mutex_q);
TmqhReleasePacketsToPacketPool(&s->slot_post_pq);
SCMutexUnlock(&s->slot_post_pq.mutex_q);
TmThreadsSetFlag(tv, THV_FAILED);
break;
}
/* handle pre queue */
while (s->slot_pre_pq.top != NULL) {
Packet *extra_p = PacketDequeue(&s->slot_pre_pq);
if (extra_p != NULL)
tv->tmqh_out(tv, extra_p);
17 years ago
}
/* handle post queue */
if (s->slot_post_pq.top != NULL) {
SCMutexLock(&s->slot_post_pq.mutex_q);
while (s->slot_post_pq.top != NULL) {
Packet *extra_p = PacketDequeue(&s->slot_post_pq);
if (extra_p != NULL)
tv->tmqh_out(tv, extra_p);
}
SCMutexUnlock(&s->slot_post_pq.mutex_q);
}
if (TmThreadsCheckFlag(tv, THV_KILL)) {
SCPerfSyncCounters(tv, 0);
run = 0;
}
} /* while (run) */
TmThreadsSetFlag(tv, THV_RUNNING_DONE);
TmThreadWaitForFlag(tv, THV_DEINIT);
if (s->SlotThreadExitPrintStats != NULL) {
s->SlotThreadExitPrintStats(tv, SC_ATOMIC_GET(s->slot_data));
}
if (s->SlotThreadDeinit != NULL) {
r = s->SlotThreadDeinit(tv, SC_ATOMIC_GET(s->slot_data));
if (r != TM_ECODE_OK) {
TmThreadsSetFlag(tv, THV_CLOSED);
pthread_exit((void *) -1);
return NULL;
}
}
TmThreadsSetFlag(tv, THV_CLOSED);
pthread_exit((void *) 0);
return NULL;
}
void *TmThreadsSlot1NoOut(void *td)
{
/* block usr2. usr2 to be handled by the main thread only */
UtilSignalBlock(SIGUSR2);
ThreadVars *tv = (ThreadVars *)td;
TmSlot *s = (TmSlot *)tv->tm_slots;
Packet *p = NULL;
char run = 1;
TmEcode r = TM_ECODE_OK;
/* Set the thread name */
if (SCSetThreadName(tv->name) < 0) {
SCLogWarning(SC_ERR_THREAD_INIT, "Unable to set thread name");
}
if (tv->thread_setup_flags != 0)
TmThreadSetupOptions(tv);
/* Drop the capabilities for this thread */
SCDropCaps(tv);
if (s->SlotThreadInit != NULL) {
void *slot_data = NULL;
r = s->SlotThreadInit(tv, s->slot_initdata, &slot_data);
if (r != TM_ECODE_OK) {
EngineKill();
TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);
pthread_exit((void *) -1);
return NULL;
}
(void)SC_ATOMIC_SET(s->slot_data, slot_data);
}
memset(&s->slot_pre_pq, 0, sizeof(PacketQueue));
memset(&s->slot_post_pq, 0, sizeof(PacketQueue));
TmThreadsSetFlag(tv, THV_INIT_DONE);
while (run) {
if (TmThreadsCheckFlag(tv, THV_PAUSE)) {
TmThreadsSetFlag(tv, THV_PAUSED);
TmThreadTestThreadUnPaused(tv);
TmThreadsUnsetFlag(tv, THV_PAUSED);
}
TmSlotFunc SlotFunc = SC_ATOMIC_GET(s->SlotFunc);
p = tv->tmqh_in(tv);
PACKET_PROFILING_TMM_START(p, s->tm_id);
r = SlotFunc(tv, p, SC_ATOMIC_GET(s->slot_data), /* no outqh no pq */ NULL,
/* no outqh no pq */ NULL);
PACKET_PROFILING_TMM_END(p, s->tm_id);
/* handle error */
if (r == TM_ECODE_FAILED) {
TmqhOutputPacketpool(tv, p);
TmThreadsSetFlag(tv, THV_FAILED);
break;
}
if (TmThreadsCheckFlag(tv, THV_KILL)) {
SCPerfSyncCounters(tv, 0);
run = 0;
}
} /* while (run) */
TmThreadsSetFlag(tv, THV_RUNNING_DONE);
TmThreadWaitForFlag(tv, THV_DEINIT);
if (s->SlotThreadExitPrintStats != NULL) {
s->SlotThreadExitPrintStats(tv, SC_ATOMIC_GET(s->slot_data));
}
if (s->SlotThreadDeinit != NULL) {
r = s->SlotThreadDeinit(tv, SC_ATOMIC_GET(s->slot_data));
if (r != TM_ECODE_OK) {
TmThreadsSetFlag(tv, THV_CLOSED);
pthread_exit((void *) -1);
return NULL;
}
}
TmThreadsSetFlag(tv, THV_CLOSED);
pthread_exit((void *) 0);
return NULL;
}
void *TmThreadsSlot1NoInOut(void *td)
{
/* block usr2. usr2 to be handled by the main thread only */
UtilSignalBlock(SIGUSR2);
ThreadVars *tv = (ThreadVars *)td;
TmSlot *s = (TmSlot *)tv->tm_slots;
char run = 1;
TmEcode r = TM_ECODE_OK;
/* Set the thread name */
if (SCSetThreadName(tv->name) < 0) {
SCLogWarning(SC_ERR_THREAD_INIT, "Unable to set thread name");
}
if (tv->thread_setup_flags != 0)
TmThreadSetupOptions(tv);
/* Drop the capabilities for this thread */
SCDropCaps(tv);
SCLogDebug("%s starting", tv->name);
if (s->SlotThreadInit != NULL) {
void *slot_data = NULL;
r = s->SlotThreadInit(tv, s->slot_initdata, &slot_data);
if (r != TM_ECODE_OK) {
EngineKill();
TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);
pthread_exit((void *) -1);
return NULL;
}
(void)SC_ATOMIC_SET(s->slot_data, slot_data);
}
memset(&s->slot_pre_pq, 0, sizeof(PacketQueue));
memset(&s->slot_post_pq, 0, sizeof(PacketQueue));
TmThreadsSetFlag(tv, THV_INIT_DONE);
while (run) {
TmSlotFunc SlotFunc = SC_ATOMIC_GET(s->SlotFunc);
if (TmThreadsCheckFlag(tv, THV_PAUSE)) {
TmThreadsSetFlag(tv, THV_PAUSED);
TmThreadTestThreadUnPaused(tv);
TmThreadsUnsetFlag(tv, THV_PAUSED);
}
r = SlotFunc(tv, NULL, SC_ATOMIC_GET(s->slot_data), /* no outqh, no pq */NULL, NULL);
/* handle error */
if (r == TM_ECODE_FAILED) {
TmThreadsSetFlag(tv, THV_FAILED);
break;
}
if (TmThreadsCheckFlag(tv, THV_KILL)) {
SCPerfSyncCounters(tv, 0);
run = 0;
}
} /* while (run) */
TmThreadsSetFlag(tv, THV_RUNNING_DONE);
TmThreadWaitForFlag(tv, THV_DEINIT);
if (s->SlotThreadExitPrintStats != NULL) {
s->SlotThreadExitPrintStats(tv, SC_ATOMIC_GET(s->slot_data));
}
if (s->SlotThreadDeinit != NULL) {
r = s->SlotThreadDeinit(tv, SC_ATOMIC_GET(s->slot_data));
if (r != TM_ECODE_OK) {
TmThreadsSetFlag(tv, THV_CLOSED);
pthread_exit((void *) -1);
return NULL;
}
}
TmThreadsSetFlag(tv, THV_CLOSED);
pthread_exit((void *) 0);
return NULL;
}
void *TmThreadsSlot1(void *td)
{
/* block usr2. usr2 to be handled by the main thread only */
UtilSignalBlock(SIGUSR2);
ThreadVars *tv = (ThreadVars *)td;
TmSlot *s = (TmSlot *)tv->tm_slots;
Packet *p = NULL;
char run = 1;
TmEcode r = TM_ECODE_OK;
/* Set the thread name */
if (SCSetThreadName(tv->name) < 0) {
SCLogWarning(SC_ERR_THREAD_INIT, "Unable to set thread name");
}
if (tv->thread_setup_flags != 0)
TmThreadSetupOptions(tv);
/* Drop the capabilities for this thread */
SCDropCaps(tv);
SCLogDebug("%s starting", tv->name);
if (s->SlotThreadInit != NULL) {
void *slot_data = NULL;
r = s->SlotThreadInit(tv, s->slot_initdata, &slot_data);
if (r != TM_ECODE_OK) {
EngineKill();
TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);
pthread_exit((void *) -1);
return NULL;
}
(void)SC_ATOMIC_SET(s->slot_data, slot_data);
}
memset(&s->slot_pre_pq, 0, sizeof(PacketQueue));
SCMutexInit(&s->slot_pre_pq.mutex_q, NULL);
memset(&s->slot_post_pq, 0, sizeof(PacketQueue));
SCMutexInit(&s->slot_post_pq.mutex_q, NULL);
TmThreadsSetFlag(tv, THV_INIT_DONE);
while (run) {
if (TmThreadsCheckFlag(tv, THV_PAUSE)) {
TmThreadsSetFlag(tv, THV_PAUSED);
TmThreadTestThreadUnPaused(tv);
TmThreadsUnsetFlag(tv, THV_PAUSED);
}
/* input a packet */
p = tv->tmqh_in(tv);
if (p != NULL) {
TmSlotFunc SlotFunc = SC_ATOMIC_GET(s->SlotFunc);
PACKET_PROFILING_TMM_START(p, s->tm_id);
r = SlotFunc(tv, p, SC_ATOMIC_GET(s->slot_data), &s->slot_pre_pq,
&s->slot_post_pq);
PACKET_PROFILING_TMM_END(p, s->tm_id);
/* handle error */
if (r == TM_ECODE_FAILED) {
TmqhReleasePacketsToPacketPool(&s->slot_pre_pq);
SCMutexLock(&s->slot_post_pq.mutex_q);
TmqhReleasePacketsToPacketPool(&s->slot_post_pq);
SCMutexUnlock(&s->slot_post_pq.mutex_q);
TmqhOutputPacketpool(tv, p);
TmThreadsSetFlag(tv, THV_FAILED);
break;
}
while (s->slot_pre_pq.top != NULL) {
/* handle new packets from this func */
Packet *extra_p = PacketDequeue(&s->slot_pre_pq);
if (extra_p != NULL) {
tv->tmqh_out(tv, extra_p);
}
17 years ago
}
/* output the packet */
tv->tmqh_out(tv, p);
}
if (s->slot_post_pq.top != NULL) {
SCMutexLock(&s->slot_post_pq.mutex_q);
while (s->slot_post_pq.top != NULL) {
/* handle new packets from this func */
Packet *extra_p = PacketDequeue(&s->slot_post_pq);
if (extra_p != NULL) {
tv->tmqh_out(tv, extra_p);
}
}
SCMutexUnlock(&s->slot_post_pq.mutex_q);
}
if (TmThreadsCheckFlag(tv, THV_KILL)) {
SCPerfSyncCounters(tv, 0);
run = 0;
}
} /* while (run) */
TmThreadsSetFlag(tv, THV_RUNNING_DONE);
TmThreadWaitForFlag(tv, THV_DEINIT);
if (s->SlotThreadExitPrintStats != NULL) {
s->SlotThreadExitPrintStats(tv, SC_ATOMIC_GET(s->slot_data));
}
if (s->SlotThreadDeinit != NULL) {
r = s->SlotThreadDeinit(tv, SC_ATOMIC_GET(s->slot_data));
if (r != TM_ECODE_OK) {
TmThreadsSetFlag(tv, THV_CLOSED);
pthread_exit((void *) -1);
return NULL;
}
}
SCLogDebug("%s ending", tv->name);
TmThreadsSetFlag(tv, THV_CLOSED);
pthread_exit((void *) 0);
return NULL;
}
/**
* \brief Separate run function so we can call it recursively.
*
* \todo Deal with post_pq for slots beyond the first.
*/
TmEcode TmThreadsSlotVarRun(ThreadVars *tv, Packet *p,
TmSlot *slot)
{
TmEcode r;
TmSlot *s;
Packet *extra_p;
17 years ago
for (s = slot; s != NULL; s = s->slot_next) {
TmSlotFunc SlotFunc = SC_ATOMIC_GET(s->SlotFunc);
PACKET_PROFILING_TMM_START(p, s->tm_id);
Add per packet profiling. Per packet profiling uses tick based accounting. It has 2 outputs, a summary and a csv file that contains per packet stats. Stats per packet include: 1) total ticks spent 2) ticks spent per individual thread module 3) "threading overhead" which is simply calculated by subtracting (2) of (1). A number of changes were made to integrate the new code in a clean way: a number of generic enums are now placed in tm-threads-common.h so we can include them from any part of the engine. Code depends on --enable-profiling just like the rule profiling code. New yaml parameters: profiling: # packet profiling packets: # Profiling can be disabled here, but it will still have a # performance impact if compiled in. enabled: yes filename: packet_stats.log append: yes # per packet csv output csv: # Output can be disabled here, but it will still have a # performance impact if compiled in. enabled: no filename: packet_stats.csv Example output of summary stats: IP ver Proto cnt min max avg ------ ----- ------ ------ ---------- ------- IPv4 6 19436 11448 5404365 32993 IPv4 256 4 11511 49968 30575 Per Thread module stats: Thread Module IP ver Proto cnt min max avg ------------------------ ------ ----- ------ ------ ---------- ------- TMM_DECODEPCAPFILE IPv4 6 19434 1242 47889 1770 TMM_DETECT IPv4 6 19436 1107 137241 1504 TMM_ALERTFASTLOG IPv4 6 19436 90 1323 155 TMM_ALERTUNIFIED2ALERT IPv4 6 19436 108 1359 138 TMM_ALERTDEBUGLOG IPv4 6 19436 90 1134 154 TMM_LOGHTTPLOG IPv4 6 19436 414 5392089 7944 TMM_STREAMTCP IPv4 6 19434 828 1299159 19438 The proto 256 is a counter for handling of pseudo/tunnel packets. Example output of csv: pcap_cnt,ipver,ipproto,total,TMM_DECODENFQ,TMM_VERDICTNFQ,TMM_RECEIVENFQ,TMM_RECEIVEPCAP,TMM_RECEIVEPCAPFILE,TMM_DECODEPCAP,TMM_DECODEPCAPFILE,TMM_RECEIVEPFRING,TMM_DECODEPFRING,TMM_DETECT,TMM_ALERTFASTLOG,TMM_ALERTFASTLOG4,TMM_ALERTFASTLOG6,TMM_ALERTUNIFIEDLOG,TMM_ALERTUNIFIEDALERT,TMM_ALERTUNIFIED2ALERT,TMM_ALERTPRELUDE,TMM_ALERTDEBUGLOG,TMM_ALERTSYSLOG,TMM_LOGDROPLOG,TMM_ALERTSYSLOG4,TMM_ALERTSYSLOG6,TMM_RESPONDREJECT,TMM_LOGHTTPLOG,TMM_LOGHTTPLOG4,TMM_LOGHTTPLOG6,TMM_PCAPLOG,TMM_STREAMTCP,TMM_DECODEIPFW,TMM_VERDICTIPFW,TMM_RECEIVEIPFW,TMM_RECEIVEERFFILE,TMM_DECODEERFFILE,TMM_RECEIVEERFDAG,TMM_DECODEERFDAG,threading 1,4,6,172008,0,0,0,0,0,0,47889,0,0,48582,1323,0,0,0,0,1359,0,1134,0,0,0,0,0,8028,0,0,0,49356,0,0,0,0,0,0,0,14337 First line of the file contains labels. 2 example gnuplot scripts added to plot the data.
14 years ago
if (unlikely(s->id == 0)) {
r = SlotFunc(tv, p, SC_ATOMIC_GET(s->slot_data), &s->slot_pre_pq, &s->slot_post_pq);
} else {
r = SlotFunc(tv, p, SC_ATOMIC_GET(s->slot_data), &s->slot_pre_pq, NULL);
}
Add per packet profiling. Per packet profiling uses tick based accounting. It has 2 outputs, a summary and a csv file that contains per packet stats. Stats per packet include: 1) total ticks spent 2) ticks spent per individual thread module 3) "threading overhead" which is simply calculated by subtracting (2) of (1). A number of changes were made to integrate the new code in a clean way: a number of generic enums are now placed in tm-threads-common.h so we can include them from any part of the engine. Code depends on --enable-profiling just like the rule profiling code. New yaml parameters: profiling: # packet profiling packets: # Profiling can be disabled here, but it will still have a # performance impact if compiled in. enabled: yes filename: packet_stats.log append: yes # per packet csv output csv: # Output can be disabled here, but it will still have a # performance impact if compiled in. enabled: no filename: packet_stats.csv Example output of summary stats: IP ver Proto cnt min max avg ------ ----- ------ ------ ---------- ------- IPv4 6 19436 11448 5404365 32993 IPv4 256 4 11511 49968 30575 Per Thread module stats: Thread Module IP ver Proto cnt min max avg ------------------------ ------ ----- ------ ------ ---------- ------- TMM_DECODEPCAPFILE IPv4 6 19434 1242 47889 1770 TMM_DETECT IPv4 6 19436 1107 137241 1504 TMM_ALERTFASTLOG IPv4 6 19436 90 1323 155 TMM_ALERTUNIFIED2ALERT IPv4 6 19436 108 1359 138 TMM_ALERTDEBUGLOG IPv4 6 19436 90 1134 154 TMM_LOGHTTPLOG IPv4 6 19436 414 5392089 7944 TMM_STREAMTCP IPv4 6 19434 828 1299159 19438 The proto 256 is a counter for handling of pseudo/tunnel packets. Example output of csv: pcap_cnt,ipver,ipproto,total,TMM_DECODENFQ,TMM_VERDICTNFQ,TMM_RECEIVENFQ,TMM_RECEIVEPCAP,TMM_RECEIVEPCAPFILE,TMM_DECODEPCAP,TMM_DECODEPCAPFILE,TMM_RECEIVEPFRING,TMM_DECODEPFRING,TMM_DETECT,TMM_ALERTFASTLOG,TMM_ALERTFASTLOG4,TMM_ALERTFASTLOG6,TMM_ALERTUNIFIEDLOG,TMM_ALERTUNIFIEDALERT,TMM_ALERTUNIFIED2ALERT,TMM_ALERTPRELUDE,TMM_ALERTDEBUGLOG,TMM_ALERTSYSLOG,TMM_LOGDROPLOG,TMM_ALERTSYSLOG4,TMM_ALERTSYSLOG6,TMM_RESPONDREJECT,TMM_LOGHTTPLOG,TMM_LOGHTTPLOG4,TMM_LOGHTTPLOG6,TMM_PCAPLOG,TMM_STREAMTCP,TMM_DECODEIPFW,TMM_VERDICTIPFW,TMM_RECEIVEIPFW,TMM_RECEIVEERFFILE,TMM_DECODEERFFILE,TMM_RECEIVEERFDAG,TMM_DECODEERFDAG,threading 1,4,6,172008,0,0,0,0,0,0,47889,0,0,48582,1323,0,0,0,0,1359,0,1134,0,0,0,0,0,8028,0,0,0,49356,0,0,0,0,0,0,0,14337 First line of the file contains labels. 2 example gnuplot scripts added to plot the data.
14 years ago
PACKET_PROFILING_TMM_END(p, s->tm_id);
Add per packet profiling. Per packet profiling uses tick based accounting. It has 2 outputs, a summary and a csv file that contains per packet stats. Stats per packet include: 1) total ticks spent 2) ticks spent per individual thread module 3) "threading overhead" which is simply calculated by subtracting (2) of (1). A number of changes were made to integrate the new code in a clean way: a number of generic enums are now placed in tm-threads-common.h so we can include them from any part of the engine. Code depends on --enable-profiling just like the rule profiling code. New yaml parameters: profiling: # packet profiling packets: # Profiling can be disabled here, but it will still have a # performance impact if compiled in. enabled: yes filename: packet_stats.log append: yes # per packet csv output csv: # Output can be disabled here, but it will still have a # performance impact if compiled in. enabled: no filename: packet_stats.csv Example output of summary stats: IP ver Proto cnt min max avg ------ ----- ------ ------ ---------- ------- IPv4 6 19436 11448 5404365 32993 IPv4 256 4 11511 49968 30575 Per Thread module stats: Thread Module IP ver Proto cnt min max avg ------------------------ ------ ----- ------ ------ ---------- ------- TMM_DECODEPCAPFILE IPv4 6 19434 1242 47889 1770 TMM_DETECT IPv4 6 19436 1107 137241 1504 TMM_ALERTFASTLOG IPv4 6 19436 90 1323 155 TMM_ALERTUNIFIED2ALERT IPv4 6 19436 108 1359 138 TMM_ALERTDEBUGLOG IPv4 6 19436 90 1134 154 TMM_LOGHTTPLOG IPv4 6 19436 414 5392089 7944 TMM_STREAMTCP IPv4 6 19434 828 1299159 19438 The proto 256 is a counter for handling of pseudo/tunnel packets. Example output of csv: pcap_cnt,ipver,ipproto,total,TMM_DECODENFQ,TMM_VERDICTNFQ,TMM_RECEIVENFQ,TMM_RECEIVEPCAP,TMM_RECEIVEPCAPFILE,TMM_DECODEPCAP,TMM_DECODEPCAPFILE,TMM_RECEIVEPFRING,TMM_DECODEPFRING,TMM_DETECT,TMM_ALERTFASTLOG,TMM_ALERTFASTLOG4,TMM_ALERTFASTLOG6,TMM_ALERTUNIFIEDLOG,TMM_ALERTUNIFIEDALERT,TMM_ALERTUNIFIED2ALERT,TMM_ALERTPRELUDE,TMM_ALERTDEBUGLOG,TMM_ALERTSYSLOG,TMM_LOGDROPLOG,TMM_ALERTSYSLOG4,TMM_ALERTSYSLOG6,TMM_RESPONDREJECT,TMM_LOGHTTPLOG,TMM_LOGHTTPLOG4,TMM_LOGHTTPLOG6,TMM_PCAPLOG,TMM_STREAMTCP,TMM_DECODEIPFW,TMM_VERDICTIPFW,TMM_RECEIVEIPFW,TMM_RECEIVEERFFILE,TMM_DECODEERFFILE,TMM_RECEIVEERFDAG,TMM_DECODEERFDAG,threading 1,4,6,172008,0,0,0,0,0,0,47889,0,0,48582,1323,0,0,0,0,1359,0,1134,0,0,0,0,0,8028,0,0,0,49356,0,0,0,0,0,0,0,14337 First line of the file contains labels. 2 example gnuplot scripts added to plot the data.
14 years ago
/* handle error */
if (unlikely(r == TM_ECODE_FAILED)) {
/* Encountered error. Return packets to packetpool and return */
TmqhReleasePacketsToPacketPool(&s->slot_pre_pq);
SCMutexLock(&s->slot_post_pq.mutex_q);
TmqhReleasePacketsToPacketPool(&s->slot_post_pq);
SCMutexUnlock(&s->slot_post_pq.mutex_q);
TmThreadsSetFlag(tv, THV_FAILED);
return TM_ECODE_FAILED;
}
17 years ago
/* handle new packets */
while (s->slot_pre_pq.top != NULL) {
extra_p = PacketDequeue(&s->slot_pre_pq);
if (unlikely(extra_p == NULL))
continue;
17 years ago
/* see if we need to process the packet */
if (s->slot_next != NULL) {
r = TmThreadsSlotVarRun(tv, extra_p, s->slot_next);
if (unlikely(r == TM_ECODE_FAILED)) {
TmqhReleasePacketsToPacketPool(&s->slot_pre_pq);
SCMutexLock(&s->slot_post_pq.mutex_q);
TmqhReleasePacketsToPacketPool(&s->slot_post_pq);
SCMutexUnlock(&s->slot_post_pq.mutex_q);
TmqhOutputPacketpool(tv, extra_p);
TmThreadsSetFlag(tv, THV_FAILED);
return TM_ECODE_FAILED;
}
17 years ago
}
tv->tmqh_out(tv, extra_p);
}
}
return TM_ECODE_OK;
17 years ago
}
/*
pcap/nfq
pkt read
callback
process_pkt
pfring
pkt read
process_pkt
slot:
setup
pkt_ack_loop(tv, slot_data)
deinit
process_pkt:
while(s)
run s;
queue;
*/
void *TmThreadsSlotPktAcqLoop(void *td) {
/* block usr2. usr2 to be handled by the main thread only */
UtilSignalBlock(SIGUSR2);
ThreadVars *tv = (ThreadVars *)td;
TmSlot *s = tv->tm_slots;
char run = 1;
TmEcode r = TM_ECODE_OK;
TmSlot *slot = NULL;
/* Set the thread name */
if (SCSetThreadName(tv->name) < 0) {
SCLogWarning(SC_ERR_THREAD_INIT, "Unable to set thread name");
}
if (tv->thread_setup_flags != 0)
TmThreadSetupOptions(tv);
/* Drop the capabilities for this thread */
SCDropCaps(tv);
/* check if we are setup properly */
if (s == NULL || s->PktAcqLoop == NULL || tv->tmqh_in == NULL || tv->tmqh_out == NULL) {
SCLogError(SC_ERR_FATAL, "TmSlot or ThreadVars badly setup: s=%p,"
" PktAcqLoop=%p, tmqh_in=%p,"
" tmqh_out=%p",
s, s ? s->PktAcqLoop : NULL, tv->tmqh_in, tv->tmqh_out);
EngineKill();
TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);
pthread_exit((void *) -1);
return NULL;
}
for (slot = s; slot != NULL; slot = slot->slot_next) {
if (slot->SlotThreadInit != NULL) {
void *slot_data = NULL;
r = slot->SlotThreadInit(tv, slot->slot_initdata, &slot_data);
if (r != TM_ECODE_OK) {
unix-manager: add unix command socket and associated script This patch introduces a unix command socket. JSON formatted messages can be exchanged between suricata and a program connecting to a dedicated socket. The protocol is the following: * Client connects to the socket * It sends a version message: { "version": "$VERSION_ID" } * Server answers with { "return": "OK|NOK" } If server returns OK, the client is now allowed to send command. The format of command is the following: { "command": "pcap-file", "arguments": { "filename": "smtp-clean.pcap", "output-dir": "/tmp/out" } } The server will try to execute the "command" specified with the (optional) provided "arguments". The answer by server is the following: { "return": "OK|NOK", "message": JSON_OBJECT or information string } A simple script is provided and is available under scripts/suricatasc. It is not intended to be enterprise-grade tool but it is more a proof of concept/example code. The first command line argument of suricatasc is used to specify the socket to connect to. Configuration of the feature is made in the YAML under the 'unix-command' section: unix-command: enabled: yes filename: custom.socket The path specified in 'filename' is not absolute and is relative to the state directory. A new running mode called 'unix-socket' is also added. When starting in this mode, only a unix socket manager is started. When it receives a 'pcap-file' command, the manager start a 'pcap-file' running mode which does not really leave at the end of file but simply exit. The manager is then able to start a new running mode with a new file. To start this mode, Suricata must be started with the --unix-socket option which has an optional argument which fix the file name of the socket. The path is not absolute and is relative to the state directory. THe 'pcap-file' command adds a file to the list of files to treat. For each pcap file, a pcap file running mode is started and the output directory is changed to what specified in the command. The running mode specified in the 'runmode' YAML setting is used to select which running mode must be use for the pcap file treatment. This requires modification in suricata.c file where initialisation code is now conditional to the fact 'unix-socket' mode is not used. Two other commands exists to get info on the remaining tasks: * pcap-file-number: return the number of files in the waiting queue * pcap-file-list: return the list of waiting files 'pcap-file-list' returns a structured object as message. The structure is the following: { 'count': 2, 'files': ['file1.pcap', 'file2.pcap'] }
14 years ago
if (r == TM_ECODE_DONE) {
EngineDone();
TmThreadsSetFlag(tv, THV_CLOSED | THV_INIT_DONE | THV_RUNNING_DONE);
pthread_exit((void *) -1);
} else {
EngineKill();
TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);
pthread_exit((void *) -1);
return NULL;
}
}
(void)SC_ATOMIC_SET(slot->slot_data, slot_data);
}
memset(&slot->slot_pre_pq, 0, sizeof(PacketQueue));
SCMutexInit(&slot->slot_pre_pq.mutex_q, NULL);
memset(&slot->slot_post_pq, 0, sizeof(PacketQueue));
SCMutexInit(&slot->slot_post_pq.mutex_q, NULL);
}
TmThreadsSetFlag(tv, THV_INIT_DONE);
while(run) {
if (TmThreadsCheckFlag(tv, THV_PAUSE)) {
TmThreadsSetFlag(tv, THV_PAUSED);
TmThreadTestThreadUnPaused(tv);
TmThreadsUnsetFlag(tv, THV_PAUSED);
}
r = s->PktAcqLoop(tv, SC_ATOMIC_GET(s->slot_data), s);
if (r == TM_ECODE_FAILED || TmThreadsCheckFlag(tv, THV_KILL)
|| suricata_ctl_flags) {
run = 0;
}
if (r == TM_ECODE_DONE) {
run = 0;
}
}
SCPerfSyncCounters(tv, 0);
TmThreadsSetFlag(tv, THV_RUNNING_DONE);
TmThreadWaitForFlag(tv, THV_DEINIT);
for (slot = s; slot != NULL; slot = slot->slot_next) {
if (slot->SlotThreadExitPrintStats != NULL) {
slot->SlotThreadExitPrintStats(tv, SC_ATOMIC_GET(slot->slot_data));
}
if (slot->SlotThreadDeinit != NULL) {
r = slot->SlotThreadDeinit(tv, SC_ATOMIC_GET(slot->slot_data));
if (r != TM_ECODE_OK) {
TmThreadsSetFlag(tv, THV_CLOSED);
pthread_exit((void *) -1);
return NULL;
}
}
}
SCLogDebug("%s ending", tv->name);
TmThreadsSetFlag(tv, THV_CLOSED);
pthread_exit((void *) 0);
return NULL;
}
/**
* \todo Only the first "slot" currently makes the "post_pq" available
* to the thread module.
*/
void *TmThreadsSlotVar(void *td)
{
/* block usr2. usr2 to be handled by the main thread only */
UtilSignalBlock(SIGUSR2);
17 years ago
ThreadVars *tv = (ThreadVars *)td;
TmSlot *s = (TmSlot *)tv->tm_slots;
17 years ago
Packet *p = NULL;
char run = 1;
TmEcode r = TM_ECODE_OK;
17 years ago
/* Set the thread name */
if (SCSetThreadName(tv->name) < 0) {
SCLogWarning(SC_ERR_THREAD_INIT, "Unable to set thread name");
}
if (tv->thread_setup_flags != 0)
TmThreadSetupOptions(tv);
/* Drop the capabilities for this thread */
SCDropCaps(tv);
/* check if we are setup properly */
if (s == NULL || tv->tmqh_in == NULL || tv->tmqh_out == NULL) {
EngineKill();
TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);
pthread_exit((void *) -1);
return NULL;
}
for (; s != NULL; s = s->slot_next) {
if (s->SlotThreadInit != NULL) {
void *slot_data = NULL;
r = s->SlotThreadInit(tv, s->slot_initdata, &slot_data);
if (r != TM_ECODE_OK) {
EngineKill();
TmThreadsSetFlag(tv, THV_CLOSED | THV_RUNNING_DONE);
17 years ago
pthread_exit((void *) -1);
return NULL;
17 years ago
}
(void)SC_ATOMIC_SET(s->slot_data, slot_data);
17 years ago
}
memset(&s->slot_pre_pq, 0, sizeof(PacketQueue));
SCMutexInit(&s->slot_pre_pq.mutex_q, NULL);
memset(&s->slot_post_pq, 0, sizeof(PacketQueue));
SCMutexInit(&s->slot_post_pq.mutex_q, NULL);
17 years ago
}
TmThreadsSetFlag(tv, THV_INIT_DONE);
s = (TmSlot *)tv->tm_slots;
while (run) {
if (TmThreadsCheckFlag(tv, THV_PAUSE)) {
TmThreadsSetFlag(tv, THV_PAUSED);
TmThreadTestThreadUnPaused(tv);
TmThreadsUnsetFlag(tv, THV_PAUSED);
}
17 years ago
/* input a packet */
p = tv->tmqh_in(tv);
if (p != NULL) {
/* run the thread module(s) */
r = TmThreadsSlotVarRun(tv, p, s);
if (r == TM_ECODE_FAILED) {
TmqhOutputPacketpool(tv, p);
TmThreadsSetFlag(tv, THV_FAILED);
break;
}
17 years ago
/* output the packet */
tv->tmqh_out(tv, p);
} /* if (p != NULL) */
/* now handle the post_pq packets */
TmSlot *slot;
for (slot = s; slot != NULL; slot = slot->slot_next) {
if (slot->slot_post_pq.top != NULL) {
while (1) {
SCMutexLock(&slot->slot_post_pq.mutex_q);
Packet *extra_p = PacketDequeue(&slot->slot_post_pq);
SCMutexUnlock(&slot->slot_post_pq.mutex_q);
if (extra_p == NULL)
break;
if (slot->slot_next != NULL) {
r = TmThreadsSlotVarRun(tv, extra_p, slot->slot_next);
if (r == TM_ECODE_FAILED) {
SCMutexLock(&slot->slot_post_pq.mutex_q);
TmqhReleasePacketsToPacketPool(&slot->slot_post_pq);
SCMutexUnlock(&slot->slot_post_pq.mutex_q);
TmqhOutputPacketpool(tv, extra_p);
TmThreadsSetFlag(tv, THV_FAILED);
break;
}
}
/* output the packet */
tv->tmqh_out(tv, extra_p);
} /* while */
} /* if */
} /* for */
17 years ago
if (TmThreadsCheckFlag(tv, THV_KILL)) {
17 years ago
run = 0;
}
} /* while (run) */
SCPerfSyncCounters(tv, 0);
17 years ago
TmThreadsSetFlag(tv, THV_RUNNING_DONE);
TmThreadWaitForFlag(tv, THV_DEINIT);
s = (TmSlot *)tv->tm_slots;
for ( ; s != NULL; s = s->slot_next) {
if (s->SlotThreadExitPrintStats != NULL) {
s->SlotThreadExitPrintStats(tv, SC_ATOMIC_GET(s->slot_data));
17 years ago
}
if (s->SlotThreadDeinit != NULL) {
r = s->SlotThreadDeinit(tv, SC_ATOMIC_GET(s->slot_data));
if (r != TM_ECODE_OK) {
TmThreadsSetFlag(tv, THV_CLOSED);
17 years ago
pthread_exit((void *) -1);
return NULL;
17 years ago
}
}
}
SCLogDebug("%s ending", tv->name);
TmThreadsSetFlag(tv, THV_CLOSED);
17 years ago
pthread_exit((void *) 0);
return NULL;
17 years ago
}
/**
* \brief We set the slot functions.
*
* \param tv Pointer to the TV to set the slot function for.
* \param name Name of the slot variant.
* \param fn_p Pointer to a custom slot function. Used only if slot variant
* "name" is "custom".
*
* \retval TmEcode TM_ECODE_OK on success; TM_ECODE_FAILED on failure.
*/
TmEcode TmThreadSetSlots(ThreadVars *tv, char *name, void *(*fn_p)(void *))
{
if (name == NULL) {
if (fn_p == NULL) {
printf("Both slot name and function pointer can't be NULL inside "
"TmThreadSetSlots\n");
goto error;
} else {
name = "custom";
}
}
if (strcmp(name, "1slot") == 0) {
tv->tm_func = TmThreadsSlot1;
} else if (strcmp(name, "1slot_noout") == 0) {
tv->tm_func = TmThreadsSlot1NoOut;
} else if (strcmp(name, "1slot_noin") == 0) {
tv->tm_func = TmThreadsSlot1NoIn;
} else if (strcmp(name, "1slot_noinout") == 0) {
tv->tm_func = TmThreadsSlot1NoInOut;
17 years ago
} else if (strcmp(name, "varslot") == 0) {
tv->tm_func = TmThreadsSlotVar;
} else if (strcmp(name, "pktacqloop") == 0) {
tv->tm_func = TmThreadsSlotPktAcqLoop;
} else if (strcmp(name, "custom") == 0) {
if (fn_p == NULL)
goto error;
tv->tm_func = fn_p;
} else {
printf("Error: Slot \"%s\" not supported\n", name);
goto error;
}
return TM_ECODE_OK;
error:
return TM_ECODE_FAILED;
}
ThreadVars *TmThreadsGetTVContainingSlot(TmSlot *tm_slot)
{
ThreadVars *tv;
int i;
SCMutexLock(&tv_root_lock);
for (i = 0; i < TVT_MAX; i++) {
tv = tv_root[i];
while (tv) {
TmSlot *slots = tv->tm_slots;
while (slots != NULL) {
if (slots == tm_slot) {
SCMutexUnlock(&tv_root_lock);
return tv;
}
slots = slots->slot_next;
}
tv = tv->next;
}
}
SCMutexUnlock(&tv_root_lock);
return NULL;
}
/**
* \brief Appends a new entry to the slots.
*
* \param tv TV the slot is attached to.
* \param tm TM to append.
* \param data Data to be passed on to the slot init function.
*
* \retval The allocated TmSlot or NULL if there is an error
*/
static inline TmSlot * _TmSlotSetFuncAppend(ThreadVars *tv, TmModule *tm, void *data)
{
TmSlot *slot = SCMalloc(sizeof(TmSlot));
if (unlikely(slot == NULL))
return NULL;
17 years ago
memset(slot, 0, sizeof(TmSlot));
SC_ATOMIC_INIT(slot->slot_data);
slot->tv = tv;
slot->SlotThreadInit = tm->ThreadInit;
slot->slot_initdata = data;
SC_ATOMIC_INIT(slot->SlotFunc);
(void)SC_ATOMIC_SET(slot->SlotFunc, tm->Func);
slot->PktAcqLoop = tm->PktAcqLoop;
slot->SlotThreadExitPrintStats = tm->ThreadExitPrintStats;
slot->SlotThreadDeinit = tm->ThreadDeinit;
/* we don't have to check for the return value "-1". We wouldn't have
* received a TM as arg, if it didn't exist */
slot->tm_id = TmModuleGetIDForTM(tm);
tv->cap_flags |= tm->cap_flags;
if (tv->tm_slots == NULL) {
tv->tm_slots = slot;
slot->id = 0;
17 years ago
} else {
TmSlot *a = (TmSlot *)tv->tm_slots, *b = NULL;
17 years ago
/* get the last slot */
for ( ; a != NULL; a = a->slot_next) {
b = a;
}
/* append the new slot */
if (b != NULL) {
b->slot_next = slot;
slot->id = b->id + 1;
17 years ago
}
}
return slot;
}
/**
* \brief Appends a new entry to the slots.
*
* \param tv TV the slot is attached to.
* \param tm TM to append.
* \param data Data to be passed on to the slot init function.
*/
void TmSlotSetFuncAppend(ThreadVars *tv, TmModule *tm, void *data)
{
_TmSlotSetFuncAppend(tv, tm, data);
}
typedef struct TmDummySlot_ {
TmSlot *slot;
TmEcode (*SlotFunc)(ThreadVars *, Packet *, void *, PacketQueue *,
PacketQueue *);
TmEcode (*SlotThreadInit)(ThreadVars *, void *, void **);
TAILQ_ENTRY(TmDummySlot_) next;
} TmDummySlot;
static TAILQ_HEAD(, TmDummySlot_) dummy_slots =
TAILQ_HEAD_INITIALIZER(dummy_slots);
/**
* \brief Appends a new entry to the slots with a delayed option.
*
* \param tv TV the slot is attached to.
* \param tm TM to append.
* \param data Data to be passed on to the slot init function.
* \param delayed Delayed start of slot if equal to 1
*/
void TmSlotSetFuncAppendDelayed(ThreadVars *tv, TmModule *tm, void *data,
int delayed)
{
TmSlot *slot = _TmSlotSetFuncAppend(tv, tm, data);
TmDummySlot *dslot = NULL;
if ((slot == NULL) || (delayed == 0)) {
return;
}
dslot = SCMalloc(sizeof(TmDummySlot));
if (unlikely(dslot == NULL)) {
return;
}
memset(dslot, 0, sizeof(*dslot));
dslot->SlotFunc = SC_ATOMIC_GET(slot->SlotFunc);
(void)SC_ATOMIC_SET(slot->SlotFunc, TmDummyFunc);
dslot->SlotThreadInit = slot->SlotThreadInit;
slot->SlotThreadInit = NULL;
dslot->slot = slot;
TAILQ_INSERT_TAIL(&dummy_slots, dslot, next);
return;
}
/**
* \brief Activate slots that have been started in delayed mode
*/
void TmThreadActivateDummySlot()
{
TmDummySlot *dslot;
TmSlot *s;
TmEcode r = TM_ECODE_OK;
TAILQ_FOREACH(dslot, &dummy_slots, next) {
void *slot_data = NULL;
s = dslot->slot;
if (dslot->SlotThreadInit != NULL) {
s->SlotThreadInit = dslot->SlotThreadInit;
r = s->SlotThreadInit(s->tv, s->slot_initdata, &slot_data);
if (r != TM_ECODE_OK) {
EngineKill();
TmThreadsSetFlag(s->tv, THV_CLOSED | THV_RUNNING_DONE);
}
(void)SC_ATOMIC_SET(s->slot_data, slot_data);
}
(void)SC_ATOMIC_CAS(&s->SlotFunc, TmDummyFunc, dslot->SlotFunc);
}
}
/**
* \brief Deactivate slots that have been started in delayed mode.
*/
void TmThreadDeActivateDummySlot()
{
TmDummySlot *dslot;
TAILQ_FOREACH(dslot, &dummy_slots, next) {
(void)SC_ATOMIC_CAS(&dslot->slot->SlotFunc, dslot->SlotFunc, TmDummyFunc);
dslot->slot->SlotThreadInit = NULL;
}
}
/**
* \brief Returns the slot holding a TM with the particular tm_id.
*
* \param tm_id TM id of the TM whose slot has to be returned.
*
* \retval slots Pointer to the slot.
*/
TmSlot *TmSlotGetSlotForTM(int tm_id)
{
ThreadVars *tv = NULL;
TmSlot *slots;
int i;
SCMutexLock(&tv_root_lock);
for (i = 0; i < TVT_MAX; i++) {
tv = tv_root[i];
while (tv) {
slots = tv->tm_slots;
while (slots != NULL) {
if (slots->tm_id == tm_id) {
SCMutexUnlock(&tv_root_lock);
return slots;
}
slots = slots->slot_next;
}
tv = tv->next;
}
}
SCMutexUnlock(&tv_root_lock);
return NULL;
}
#if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__
static int SetCPUAffinitySet(cpu_set_t *cs) {
#if defined OS_FREEBSD
int r = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
SCGetThreadIdLong(), sizeof(cpu_set_t),cs);
#elif OS_DARWIN
int r = thread_policy_set(mach_thread_self(), THREAD_AFFINITY_POLICY,
(void*)cs, THREAD_AFFINITY_POLICY_COUNT);
#else
pid_t tid = syscall(SYS_gettid);
int r = sched_setaffinity(tid, sizeof(cpu_set_t), cs);
#endif /* OS_FREEBSD */
if (r != 0) {
printf("Warning: sched_setaffinity failed (%" PRId32 "): %s\n", r,
strerror(errno));
return -1;
}
return 0;
}
#endif
/**
* \brief Set the thread affinity on the calling thread.
*
* \param cpuid Id of the core/cpu to setup the affinity.
*
* \retval 0 If all goes well; -1 if something is wrong.
*/
static int SetCPUAffinity(uint16_t cpuid)
{
#if defined __OpenBSD__
return 0;
#else
int cpu = (int)cpuid;
#if defined OS_WIN32 || defined __CYGWIN__
DWORD cs = 1 << cpu;
int r = (0 == SetThreadAffinityMask(GetCurrentThread(), cs));
if (r != 0) {
printf("Warning: sched_setaffinity failed (%" PRId32 "): %s\n", r,
strerror(errno));
return -1;
}
SCLogDebug("CPU Affinity for thread %lu set to CPU %" PRId32,
SCGetThreadIdLong(), cpu);
return 0;
#else
cpu_set_t cs;
CPU_ZERO(&cs);
CPU_SET(cpu, &cs);
return SetCPUAffinitySet(&cs);
#endif /* windows */
#endif /* not supported */
}
/**
* \brief Set the thread options (thread priority).
*
* \param tv Pointer to the ThreadVars to setup the thread priority.
*
* \retval TM_ECODE_OK.
*/
TmEcode TmThreadSetThreadPriority(ThreadVars *tv, int prio)
{
tv->thread_setup_flags |= THREAD_SET_PRIORITY;
tv->thread_priority = prio;
return TM_ECODE_OK;
}
/**
* \brief Adjusting nice value for threads.
*/
void TmThreadSetPrio(ThreadVars *tv)
{
SCEnter();
#ifndef __CYGWIN__
#ifdef OS_WIN32
if (0 == SetThreadPriority(GetCurrentThread(), tv->thread_priority)) {
SCLogError(SC_ERR_THREAD_NICE_PRIO, "Error setting priority for "
"thread %s: %s", tv->name, strerror(errno));
} else {
SCLogDebug("Priority set to %"PRId32" for thread %s",
tv->thread_priority, tv->name);
}
#else
int ret = nice(tv->thread_priority);
if (ret == -1) {
SCLogError(SC_ERR_THREAD_NICE_PRIO, "Error setting nice value "
"for thread %s: %s", tv->name, strerror(errno));
} else {
SCLogDebug("Nice value set to %"PRId32" for thread %s",
tv->thread_priority, tv->name);
}
#endif /* OS_WIN32 */
#endif
SCReturn;
}
/**
* \brief Set the thread options (cpu affinity).
*
* \param tv pointer to the ThreadVars to setup the affinity.
* \param cpu cpu on which affinity is set.
*
* \retval TM_ECODE_OK
*/
TmEcode TmThreadSetCPUAffinity(ThreadVars *tv, uint16_t cpu)
{
tv->thread_setup_flags |= THREAD_SET_AFFINITY;
tv->cpu_affinity = cpu;
return TM_ECODE_OK;
}
TmEcode TmThreadSetCPU(ThreadVars *tv, uint8_t type)
{
if (!threading_set_cpu_affinity)
return TM_ECODE_OK;
if (type > MAX_CPU_SET) {
SCLogError(SC_ERR_INVALID_ARGUMENT, "invalid cpu type family");
return TM_ECODE_FAILED;
}
tv->thread_setup_flags |= THREAD_SET_AFFTYPE;
tv->cpu_affinity = type;
return TM_ECODE_OK;
}
int TmThreadGetNbThreads(uint8_t type)
{
if (type >= MAX_CPU_SET) {
SCLogError(SC_ERR_INVALID_ARGUMENT, "invalid cpu type family");
return 0;
}
return thread_affinity[type].nb_threads;
}
/**
* \brief Set the thread options (cpu affinitythread).
* Priority should be already set by pthread_create.
*
* \param tv pointer to the ThreadVars of the calling thread.
*/
TmEcode TmThreadSetupOptions(ThreadVars *tv)
{
if (tv->thread_setup_flags & THREAD_SET_AFFINITY) {
SCLogInfo("Setting affinity for \"%s\" Module to cpu/core "
"%"PRIu16", thread id %lu", tv->name, tv->cpu_affinity,
SCGetThreadIdLong());
SetCPUAffinity(tv->cpu_affinity);
}
#if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__
if (tv->thread_setup_flags & THREAD_SET_PRIORITY)
TmThreadSetPrio(tv);
if (tv->thread_setup_flags & THREAD_SET_AFFTYPE) {
ThreadsAffinityType *taf = &thread_affinity[tv->cpu_affinity];
if (taf->mode_flag == EXCLUSIVE_AFFINITY) {
int cpu = AffinityGetNextCPU(taf);
SetCPUAffinity(cpu);
/* If CPU is in a set overwrite the default thread prio */
if (CPU_ISSET(cpu, &taf->lowprio_cpu)) {
tv->thread_priority = PRIO_LOW;
} else if (CPU_ISSET(cpu, &taf->medprio_cpu)) {
tv->thread_priority = PRIO_MEDIUM;
} else if (CPU_ISSET(cpu, &taf->hiprio_cpu)) {
tv->thread_priority = PRIO_HIGH;
} else {
tv->thread_priority = taf->prio;
}
SCLogInfo("Setting prio %d for \"%s\" Module to cpu/core "
"%"PRIu16", thread id %lu", tv->thread_priority,
tv->name, cpu, SCGetThreadIdLong());
} else {
SetCPUAffinitySet(&taf->cpu_set);
tv->thread_priority = taf->prio;
SCLogInfo("Setting prio %d for \"%s\" thread "
", thread id %lu", tv->thread_priority,
tv->name, SCGetThreadIdLong());
}
TmThreadSetPrio(tv);
}
#endif
return TM_ECODE_OK;
}
/**
* \brief Creates and returns the TV instance for a new thread.
*
* \param name Name of this TV instance
* \param inq_name Incoming queue name
* \param inqh_name Incoming queue handler name as set by TmqhSetup()
* \param outq_name Outgoing queue name
* \param outqh_name Outgoing queue handler as set by TmqhSetup()
* \param slots String representation for the slot function to be used
* \param fn_p Pointer to function when \"slots\" is of type \"custom\"
* \param mucond Flag to indicate whether to initialize the condition
* and the mutex variables for this newly created TV.
*
* \retval the newly created TV instance, or NULL on error
*/
ThreadVars *TmThreadCreate(char *name, char *inq_name, char *inqh_name,
char *outq_name, char *outqh_name, char *slots,
void * (*fn_p)(void *), int mucond)
{
ThreadVars *tv = NULL;
Tmq *tmq = NULL;
Tmqh *tmqh = NULL;
SCLogDebug("creating thread \"%s\"...", name);
/* XXX create separate function for this: allocate a thread container */
tv = SCMalloc(sizeof(ThreadVars));
if (unlikely(tv == NULL))
goto error;
memset(tv, 0, sizeof(ThreadVars));
SC_ATOMIC_INIT(tv->flags);
SCMutexInit(&tv->sc_perf_pctx.m, NULL);
tv->name = name;
/* default state for every newly created thread */
TmThreadsSetFlag(tv, THV_PAUSE);
TmThreadsSetFlag(tv, THV_USE);
/* default aof for every newly created thread */
tv->aof = THV_RESTART_THREAD;
/* set the incoming queue */
if (inq_name != NULL && strcmp(inq_name, "packetpool") != 0) {
SCLogDebug("inq_name \"%s\"", inq_name);
tmq = TmqGetQueueByName(inq_name);
if (tmq == NULL) {
tmq = TmqCreateQueue(inq_name);
if (tmq == NULL)
goto error;
}
SCLogDebug("tmq %p", tmq);
tv->inq = tmq;
tv->inq->reader_cnt++;
SCLogDebug("tv->inq %p", tv->inq);
}
if (inqh_name != NULL) {
SCLogDebug("inqh_name \"%s\"", inqh_name);
tmqh = TmqhGetQueueHandlerByName(inqh_name);
if (tmqh == NULL)
goto error;
tv->tmqh_in = tmqh->InHandler;
tv->InShutdownHandler = tmqh->InShutdownHandler;
SCLogDebug("tv->tmqh_in %p", tv->tmqh_in);
}
/* set the outgoing queue */
if (outqh_name != NULL) {
SCLogDebug("outqh_name \"%s\"", outqh_name);
tmqh = TmqhGetQueueHandlerByName(outqh_name);
if (tmqh == NULL)
goto error;
tv->tmqh_out = tmqh->OutHandler;
tv->outqh_name = tmqh->name;
if (outq_name != NULL && strcmp(outq_name, "packetpool") != 0) {
SCLogDebug("outq_name \"%s\"", outq_name);
if (tmqh->OutHandlerCtxSetup != NULL) {
tv->outctx = tmqh->OutHandlerCtxSetup(outq_name);
tv->outq = NULL;
} else {
tmq = TmqGetQueueByName(outq_name);
if (tmq == NULL) {
tmq = TmqCreateQueue(outq_name);
if (tmq == NULL)
goto error;
}
SCLogDebug("tmq %p", tmq);
tv->outq = tmq;
tv->outctx = NULL;
tv->outq->writer_cnt++;
}
}
}
if (TmThreadSetSlots(tv, slots, fn_p) != TM_ECODE_OK) {
goto error;
}
if (mucond != 0)
TmThreadInitMC(tv);
return tv;
error:
SCLogError(SC_ERR_THREAD_CREATE, "failed to setup a thread");
if (tv != NULL)
SCFree(tv);
return NULL;
}
/**
* \brief Creates and returns a TV instance for a Packet Processing Thread.
* This function doesn't support custom slots, and hence shouldn't be
* supplied \"custom\" as its slot type. All PPT threads are created
* with a mucond(see TmThreadCreate declaration) of 0. Hence the tv
* conditional variables are not used to kill the thread.
*
* \param name Name of this TV instance
* \param inq_name Incoming queue name
* \param inqh_name Incoming queue handler name as set by TmqhSetup()
* \param outq_name Outgoing queue name
* \param outqh_name Outgoing queue handler as set by TmqhSetup()
* \param slots String representation for the slot function to be used
*
* \retval the newly created TV instance, or NULL on error
*/
ThreadVars *TmThreadCreatePacketHandler(char *name, char *inq_name,
char *inqh_name, char *outq_name,
char *outqh_name, char *slots)
{
ThreadVars *tv = NULL;
tv = TmThreadCreate(name, inq_name, inqh_name, outq_name, outqh_name,
slots, NULL, 0);
if (tv != NULL)
tv->type = TVT_PPT;
return tv;
}
/**
* \brief Creates and returns the TV instance for a Management thread(MGMT).
* This function supports only custom slot functions and hence a
* function pointer should be sent as an argument.
*
* \param name Name of this TV instance
* \param fn_p Pointer to function when \"slots\" is of type \"custom\"
* \param mucond Flag to indicate whether to initialize the condition
* and the mutex variables for this newly created TV.
*
* \retval the newly created TV instance, or NULL on error
*/
ThreadVars *TmThreadCreateMgmtThread(char *name, void *(fn_p)(void *),
int mucond)
{
ThreadVars *tv = NULL;
tv = TmThreadCreate(name, NULL, NULL, NULL, NULL, "custom", fn_p, mucond);
if (tv != NULL) {
tv->type = TVT_MGMT;
TmThreadSetCPU(tv, MANAGEMENT_CPU_SET);
}
return tv;
}
unix-manager: add unix command socket and associated script This patch introduces a unix command socket. JSON formatted messages can be exchanged between suricata and a program connecting to a dedicated socket. The protocol is the following: * Client connects to the socket * It sends a version message: { "version": "$VERSION_ID" } * Server answers with { "return": "OK|NOK" } If server returns OK, the client is now allowed to send command. The format of command is the following: { "command": "pcap-file", "arguments": { "filename": "smtp-clean.pcap", "output-dir": "/tmp/out" } } The server will try to execute the "command" specified with the (optional) provided "arguments". The answer by server is the following: { "return": "OK|NOK", "message": JSON_OBJECT or information string } A simple script is provided and is available under scripts/suricatasc. It is not intended to be enterprise-grade tool but it is more a proof of concept/example code. The first command line argument of suricatasc is used to specify the socket to connect to. Configuration of the feature is made in the YAML under the 'unix-command' section: unix-command: enabled: yes filename: custom.socket The path specified in 'filename' is not absolute and is relative to the state directory. A new running mode called 'unix-socket' is also added. When starting in this mode, only a unix socket manager is started. When it receives a 'pcap-file' command, the manager start a 'pcap-file' running mode which does not really leave at the end of file but simply exit. The manager is then able to start a new running mode with a new file. To start this mode, Suricata must be started with the --unix-socket option which has an optional argument which fix the file name of the socket. The path is not absolute and is relative to the state directory. THe 'pcap-file' command adds a file to the list of files to treat. For each pcap file, a pcap file running mode is started and the output directory is changed to what specified in the command. The running mode specified in the 'runmode' YAML setting is used to select which running mode must be use for the pcap file treatment. This requires modification in suricata.c file where initialisation code is now conditional to the fact 'unix-socket' mode is not used. Two other commands exists to get info on the remaining tasks: * pcap-file-number: return the number of files in the waiting queue * pcap-file-list: return the list of waiting files 'pcap-file-list' returns a structured object as message. The structure is the following: { 'count': 2, 'files': ['file1.pcap', 'file2.pcap'] }
14 years ago
/**
* \brief Creates and returns the TV instance for a CMD thread.
* This function supports only custom slot functions and hence a
* function pointer should be sent as an argument.
*
* \param name Name of this TV instance
* \param fn_p Pointer to function when \"slots\" is of type \"custom\"
* \param mucond Flag to indicate whether to initialize the condition
* and the mutex variables for this newly created TV.
*
* \retval the newly created TV instance, or NULL on error
*/
ThreadVars *TmThreadCreateCmdThread(char *name, void *(fn_p)(void *),
int mucond)
{
ThreadVars *tv = NULL;
tv = TmThreadCreate(name, NULL, NULL, NULL, NULL, "custom", fn_p, mucond);
if (tv != NULL) {
tv->type = TVT_CMD;
TmThreadSetCPU(tv, MANAGEMENT_CPU_SET);
}
return tv;
}
/**
* \brief Appends this TV to tv_root based on its type
*
* \param type holds the type this TV belongs to.
*/
void TmThreadAppend(ThreadVars *tv, int type)
{
SCMutexLock(&tv_root_lock);
if (tv_root[type] == NULL) {
tv_root[type] = tv;
tv->next = NULL;
tv->prev = NULL;
SCMutexUnlock(&tv_root_lock);
return;
}
ThreadVars *t = tv_root[type];
while (t) {
if (t->next == NULL) {
t->next = tv;
tv->prev = t;
tv->next = NULL;
break;
}
t = t->next;
}
SCMutexUnlock(&tv_root_lock);
return;
}
/**
* \brief Removes this TV from tv_root based on its type
*
* \param tv The tv instance to remove from the global tv list.
* \param type Holds the type this TV belongs to.
*/
void TmThreadRemove(ThreadVars *tv, int type)
{
SCMutexLock(&tv_root_lock);
if (tv_root[type] == NULL) {
SCMutexUnlock(&tv_root_lock);
return;
}
ThreadVars *t = tv_root[type];
while (t != tv) {
t = t->next;
}
if (t != NULL) {
if (t->prev != NULL)
t->prev->next = t->next;
if (t->next != NULL)
t->next->prev = t->prev;
if (t == tv_root[type])
tv_root[type] = t->next;;
}
SCMutexUnlock(&tv_root_lock);
return;
}
/**
* \brief Kill a thread.
*
* \param tv A ThreadVars instance corresponding to the thread that has to be
* killed.
*/
void TmThreadKillThread(ThreadVars *tv)
{
int i = 0;
if (tv == NULL)
return;
if (tv->inq != NULL) {
/* we wait till we dry out all the inq packets, before we
* kill this thread. Do note that you should have disabled
* packet acquire by now using TmThreadDisableReceiveThreads()*/
if (!(strlen(tv->inq->name) == strlen("packetpool") &&
strcasecmp(tv->inq->name, "packetpool") == 0)) {
PacketQueue *q = &trans_q[tv->inq->id];
while (q->len != 0) {
usleep(1000);
}
}
}
/* set the thread flag informing the thread that it needs to be
* terminated */
TmThreadsSetFlag(tv, THV_KILL);
TmThreadsSetFlag(tv, THV_DEINIT);
/* to be sure, signal more */
int cnt = 0;
while (1) {
if (TmThreadsCheckFlag(tv, THV_CLOSED)) {
SCLogDebug("signalled the thread %" PRId32 " times", cnt);
break;
}
cnt++;
if (tv->InShutdownHandler != NULL) {
tv->InShutdownHandler(tv);
}
if (tv->inq != NULL) {
for (i = 0; i < (tv->inq->reader_cnt + tv->inq->writer_cnt); i++) {
if (tv->inq->q_type == 0)
SCCondSignal(&trans_q[tv->inq->id].cond_q);
else
SCCondSignal(&data_queues[tv->inq->id].cond_q);
}
SCLogDebug("signalled tv->inq->id %" PRIu32 "", tv->inq->id);
}
if (tv->cond != NULL ) {
pthread_cond_broadcast(tv->cond);
}
usleep(100);
}
if (tv->outctx != NULL) {
Tmqh *tmqh = TmqhGetQueueHandlerByName(tv->outqh_name);
if (tmqh == NULL)
BUG_ON(1);
if (tmqh->OutHandlerCtxFree != NULL) {
tmqh->OutHandlerCtxFree(tv->outctx);
}
}
/* join it */
pthread_join(tv->t, NULL);
SCLogDebug("thread %s stopped", tv->name);
return;
}
/**
* \brief Disable all threads having the specified TMs.
*/
void TmThreadDisableThreadsWithTMS(uint8_t tm_flags)
{
/* value in seconds */
#define THREAD_KILL_MAX_WAIT_TIME 60
/* value in microseconds */
#define WAIT_TIME 100
double total_wait_time = 0;
ThreadVars *tv = NULL;
SCMutexLock(&tv_root_lock);
/* all receive threads are part of packet processing threads */
tv = tv_root[TVT_PPT];
/* we do have to keep in mind that TVs are arranged in the order
* right from receive to log. The moment we fail to find a
* receive TM amongst the slots in a tv, it indicates we are done
* with all receive threads */
while (tv) {
int disable = 0;
/* obtain the slots for this TV */
TmSlot *slots = tv->tm_slots;
while (slots != NULL) {
TmModule *tm = TmModuleGetById(slots->tm_id);
if (tm->flags & tm_flags) {
disable = 1;
break;
}
slots = slots->slot_next;
continue;
}
if (disable) {
if (tv->inq != NULL) {
/* we wait till we dry out all the inq packets, before we
* kill this thread. Do note that you should have disabled
* packet acquire by now using TmThreadDisableReceiveThreads()*/
if (!(strlen(tv->inq->name) == strlen("packetpool") &&
strcasecmp(tv->inq->name, "packetpool") == 0)) {
PacketQueue *q = &trans_q[tv->inq->id];
while (q->len != 0) {
usleep(1000);
}
}
}
/* we found our receive TV. Send it a KILL signal. This is all
* we need to do to kill receive threads */
TmThreadsSetFlag(tv, THV_KILL);
if (tv->inq != NULL) {
int i;
for (i = 0; i < (tv->inq->reader_cnt + tv->inq->writer_cnt); i++) {
if (tv->inq->q_type == 0)
SCCondSignal(&trans_q[tv->inq->id].cond_q);
else
SCCondSignal(&data_queues[tv->inq->id].cond_q);
}
SCLogDebug("signalled tv->inq->id %" PRIu32 "", tv->inq->id);
}
while (!TmThreadsCheckFlag(tv, THV_RUNNING_DONE)) {
usleep(WAIT_TIME);
total_wait_time += WAIT_TIME / 1000000.0;
if (total_wait_time > THREAD_KILL_MAX_WAIT_TIME) {
SCLogError(SC_ERR_FATAL, "Engine unable to "
"disable detect thread - \"%s\". "
"Killing engine", tv->name);
exit(EXIT_FAILURE);
}
}
}
tv = tv->next;
}
SCMutexUnlock(&tv_root_lock);
return;
}
TmSlot *TmThreadGetFirstTmSlotForPartialPattern(const char *tm_name)
{
ThreadVars *tv = NULL;
TmSlot *slots = NULL;
SCMutexLock(&tv_root_lock);
/* all receive threads are part of packet processing threads */
tv = tv_root[TVT_PPT];
while (tv) {
slots = tv->tm_slots;
while (slots != NULL) {
TmModule *tm = TmModuleGetById(slots->tm_id);
char *found = strstr(tm->name, tm_name);
if (found != NULL)
goto end;
slots = slots->slot_next;
}
tv = tv->next;
}
end:
SCMutexUnlock(&tv_root_lock);
return slots;
}
unix-manager: add unix command socket and associated script This patch introduces a unix command socket. JSON formatted messages can be exchanged between suricata and a program connecting to a dedicated socket. The protocol is the following: * Client connects to the socket * It sends a version message: { "version": "$VERSION_ID" } * Server answers with { "return": "OK|NOK" } If server returns OK, the client is now allowed to send command. The format of command is the following: { "command": "pcap-file", "arguments": { "filename": "smtp-clean.pcap", "output-dir": "/tmp/out" } } The server will try to execute the "command" specified with the (optional) provided "arguments". The answer by server is the following: { "return": "OK|NOK", "message": JSON_OBJECT or information string } A simple script is provided and is available under scripts/suricatasc. It is not intended to be enterprise-grade tool but it is more a proof of concept/example code. The first command line argument of suricatasc is used to specify the socket to connect to. Configuration of the feature is made in the YAML under the 'unix-command' section: unix-command: enabled: yes filename: custom.socket The path specified in 'filename' is not absolute and is relative to the state directory. A new running mode called 'unix-socket' is also added. When starting in this mode, only a unix socket manager is started. When it receives a 'pcap-file' command, the manager start a 'pcap-file' running mode which does not really leave at the end of file but simply exit. The manager is then able to start a new running mode with a new file. To start this mode, Suricata must be started with the --unix-socket option which has an optional argument which fix the file name of the socket. The path is not absolute and is relative to the state directory. THe 'pcap-file' command adds a file to the list of files to treat. For each pcap file, a pcap file running mode is started and the output directory is changed to what specified in the command. The running mode specified in the 'runmode' YAML setting is used to select which running mode must be use for the pcap file treatment. This requires modification in suricata.c file where initialisation code is now conditional to the fact 'unix-socket' mode is not used. Two other commands exists to get info on the remaining tasks: * pcap-file-number: return the number of files in the waiting queue * pcap-file-list: return the list of waiting files 'pcap-file-list' returns a structured object as message. The structure is the following: { 'count': 2, 'files': ['file1.pcap', 'file2.pcap'] }
14 years ago
void TmThreadKillThreadsFamily(int family)
{
ThreadVars *tv = NULL;
unix-manager: add unix command socket and associated script This patch introduces a unix command socket. JSON formatted messages can be exchanged between suricata and a program connecting to a dedicated socket. The protocol is the following: * Client connects to the socket * It sends a version message: { "version": "$VERSION_ID" } * Server answers with { "return": "OK|NOK" } If server returns OK, the client is now allowed to send command. The format of command is the following: { "command": "pcap-file", "arguments": { "filename": "smtp-clean.pcap", "output-dir": "/tmp/out" } } The server will try to execute the "command" specified with the (optional) provided "arguments". The answer by server is the following: { "return": "OK|NOK", "message": JSON_OBJECT or information string } A simple script is provided and is available under scripts/suricatasc. It is not intended to be enterprise-grade tool but it is more a proof of concept/example code. The first command line argument of suricatasc is used to specify the socket to connect to. Configuration of the feature is made in the YAML under the 'unix-command' section: unix-command: enabled: yes filename: custom.socket The path specified in 'filename' is not absolute and is relative to the state directory. A new running mode called 'unix-socket' is also added. When starting in this mode, only a unix socket manager is started. When it receives a 'pcap-file' command, the manager start a 'pcap-file' running mode which does not really leave at the end of file but simply exit. The manager is then able to start a new running mode with a new file. To start this mode, Suricata must be started with the --unix-socket option which has an optional argument which fix the file name of the socket. The path is not absolute and is relative to the state directory. THe 'pcap-file' command adds a file to the list of files to treat. For each pcap file, a pcap file running mode is started and the output directory is changed to what specified in the command. The running mode specified in the 'runmode' YAML setting is used to select which running mode must be use for the pcap file treatment. This requires modification in suricata.c file where initialisation code is now conditional to the fact 'unix-socket' mode is not used. Two other commands exists to get info on the remaining tasks: * pcap-file-number: return the number of files in the waiting queue * pcap-file-list: return the list of waiting files 'pcap-file-list' returns a structured object as message. The structure is the following: { 'count': 2, 'files': ['file1.pcap', 'file2.pcap'] }
14 years ago
if ((family < 0) || (family >= TVT_MAX))
return;
tv = tv_root[family];
while (tv) {
TmThreadKillThread(tv);
tv = tv->next;
}
}
void TmThreadKillThreads(void)
{
int i = 0;
for (i = 0; i < TVT_MAX; i++) {
unix-manager: add unix command socket and associated script This patch introduces a unix command socket. JSON formatted messages can be exchanged between suricata and a program connecting to a dedicated socket. The protocol is the following: * Client connects to the socket * It sends a version message: { "version": "$VERSION_ID" } * Server answers with { "return": "OK|NOK" } If server returns OK, the client is now allowed to send command. The format of command is the following: { "command": "pcap-file", "arguments": { "filename": "smtp-clean.pcap", "output-dir": "/tmp/out" } } The server will try to execute the "command" specified with the (optional) provided "arguments". The answer by server is the following: { "return": "OK|NOK", "message": JSON_OBJECT or information string } A simple script is provided and is available under scripts/suricatasc. It is not intended to be enterprise-grade tool but it is more a proof of concept/example code. The first command line argument of suricatasc is used to specify the socket to connect to. Configuration of the feature is made in the YAML under the 'unix-command' section: unix-command: enabled: yes filename: custom.socket The path specified in 'filename' is not absolute and is relative to the state directory. A new running mode called 'unix-socket' is also added. When starting in this mode, only a unix socket manager is started. When it receives a 'pcap-file' command, the manager start a 'pcap-file' running mode which does not really leave at the end of file but simply exit. The manager is then able to start a new running mode with a new file. To start this mode, Suricata must be started with the --unix-socket option which has an optional argument which fix the file name of the socket. The path is not absolute and is relative to the state directory. THe 'pcap-file' command adds a file to the list of files to treat. For each pcap file, a pcap file running mode is started and the output directory is changed to what specified in the command. The running mode specified in the 'runmode' YAML setting is used to select which running mode must be use for the pcap file treatment. This requires modification in suricata.c file where initialisation code is now conditional to the fact 'unix-socket' mode is not used. Two other commands exists to get info on the remaining tasks: * pcap-file-number: return the number of files in the waiting queue * pcap-file-list: return the list of waiting files 'pcap-file-list' returns a structured object as message. The structure is the following: { 'count': 2, 'files': ['file1.pcap', 'file2.pcap'] }
14 years ago
TmThreadKillThreadsFamily(i);
}
unix-manager: add unix command socket and associated script This patch introduces a unix command socket. JSON formatted messages can be exchanged between suricata and a program connecting to a dedicated socket. The protocol is the following: * Client connects to the socket * It sends a version message: { "version": "$VERSION_ID" } * Server answers with { "return": "OK|NOK" } If server returns OK, the client is now allowed to send command. The format of command is the following: { "command": "pcap-file", "arguments": { "filename": "smtp-clean.pcap", "output-dir": "/tmp/out" } } The server will try to execute the "command" specified with the (optional) provided "arguments". The answer by server is the following: { "return": "OK|NOK", "message": JSON_OBJECT or information string } A simple script is provided and is available under scripts/suricatasc. It is not intended to be enterprise-grade tool but it is more a proof of concept/example code. The first command line argument of suricatasc is used to specify the socket to connect to. Configuration of the feature is made in the YAML under the 'unix-command' section: unix-command: enabled: yes filename: custom.socket The path specified in 'filename' is not absolute and is relative to the state directory. A new running mode called 'unix-socket' is also added. When starting in this mode, only a unix socket manager is started. When it receives a 'pcap-file' command, the manager start a 'pcap-file' running mode which does not really leave at the end of file but simply exit. The manager is then able to start a new running mode with a new file. To start this mode, Suricata must be started with the --unix-socket option which has an optional argument which fix the file name of the socket. The path is not absolute and is relative to the state directory. THe 'pcap-file' command adds a file to the list of files to treat. For each pcap file, a pcap file running mode is started and the output directory is changed to what specified in the command. The running mode specified in the 'runmode' YAML setting is used to select which running mode must be use for the pcap file treatment. This requires modification in suricata.c file where initialisation code is now conditional to the fact 'unix-socket' mode is not used. Two other commands exists to get info on the remaining tasks: * pcap-file-number: return the number of files in the waiting queue * pcap-file-list: return the list of waiting files 'pcap-file-list' returns a structured object as message. The structure is the following: { 'count': 2, 'files': ['file1.pcap', 'file2.pcap'] }
14 years ago
return;
}
unix-manager: add unix command socket and associated script This patch introduces a unix command socket. JSON formatted messages can be exchanged between suricata and a program connecting to a dedicated socket. The protocol is the following: * Client connects to the socket * It sends a version message: { "version": "$VERSION_ID" } * Server answers with { "return": "OK|NOK" } If server returns OK, the client is now allowed to send command. The format of command is the following: { "command": "pcap-file", "arguments": { "filename": "smtp-clean.pcap", "output-dir": "/tmp/out" } } The server will try to execute the "command" specified with the (optional) provided "arguments". The answer by server is the following: { "return": "OK|NOK", "message": JSON_OBJECT or information string } A simple script is provided and is available under scripts/suricatasc. It is not intended to be enterprise-grade tool but it is more a proof of concept/example code. The first command line argument of suricatasc is used to specify the socket to connect to. Configuration of the feature is made in the YAML under the 'unix-command' section: unix-command: enabled: yes filename: custom.socket The path specified in 'filename' is not absolute and is relative to the state directory. A new running mode called 'unix-socket' is also added. When starting in this mode, only a unix socket manager is started. When it receives a 'pcap-file' command, the manager start a 'pcap-file' running mode which does not really leave at the end of file but simply exit. The manager is then able to start a new running mode with a new file. To start this mode, Suricata must be started with the --unix-socket option which has an optional argument which fix the file name of the socket. The path is not absolute and is relative to the state directory. THe 'pcap-file' command adds a file to the list of files to treat. For each pcap file, a pcap file running mode is started and the output directory is changed to what specified in the command. The running mode specified in the 'runmode' YAML setting is used to select which running mode must be use for the pcap file treatment. This requires modification in suricata.c file where initialisation code is now conditional to the fact 'unix-socket' mode is not used. Two other commands exists to get info on the remaining tasks: * pcap-file-number: return the number of files in the waiting queue * pcap-file-list: return the list of waiting files 'pcap-file-list' returns a structured object as message. The structure is the following: { 'count': 2, 'files': ['file1.pcap', 'file2.pcap'] }
14 years ago
void TmThreadFree(ThreadVars *tv)
{
TmSlot *s;
TmSlot *ps;
if (tv == NULL)
return;
SCLogDebug("Freeing thread '%s'.", tv->name);
SCMutexDestroy(&tv->sc_perf_pctx.m);
s = (TmSlot *)tv->tm_slots;
while (s) {
ps = s;
s = s->slot_next;
SCFree(ps);
}
unix-manager: add unix command socket and associated script This patch introduces a unix command socket. JSON formatted messages can be exchanged between suricata and a program connecting to a dedicated socket. The protocol is the following: * Client connects to the socket * It sends a version message: { "version": "$VERSION_ID" } * Server answers with { "return": "OK|NOK" } If server returns OK, the client is now allowed to send command. The format of command is the following: { "command": "pcap-file", "arguments": { "filename": "smtp-clean.pcap", "output-dir": "/tmp/out" } } The server will try to execute the "command" specified with the (optional) provided "arguments". The answer by server is the following: { "return": "OK|NOK", "message": JSON_OBJECT or information string } A simple script is provided and is available under scripts/suricatasc. It is not intended to be enterprise-grade tool but it is more a proof of concept/example code. The first command line argument of suricatasc is used to specify the socket to connect to. Configuration of the feature is made in the YAML under the 'unix-command' section: unix-command: enabled: yes filename: custom.socket The path specified in 'filename' is not absolute and is relative to the state directory. A new running mode called 'unix-socket' is also added. When starting in this mode, only a unix socket manager is started. When it receives a 'pcap-file' command, the manager start a 'pcap-file' running mode which does not really leave at the end of file but simply exit. The manager is then able to start a new running mode with a new file. To start this mode, Suricata must be started with the --unix-socket option which has an optional argument which fix the file name of the socket. The path is not absolute and is relative to the state directory. THe 'pcap-file' command adds a file to the list of files to treat. For each pcap file, a pcap file running mode is started and the output directory is changed to what specified in the command. The running mode specified in the 'runmode' YAML setting is used to select which running mode must be use for the pcap file treatment. This requires modification in suricata.c file where initialisation code is now conditional to the fact 'unix-socket' mode is not used. Two other commands exists to get info on the remaining tasks: * pcap-file-number: return the number of files in the waiting queue * pcap-file-list: return the list of waiting files 'pcap-file-list' returns a structured object as message. The structure is the following: { 'count': 2, 'files': ['file1.pcap', 'file2.pcap'] }
14 years ago
SCFree(tv);
}
unix-manager: add unix command socket and associated script This patch introduces a unix command socket. JSON formatted messages can be exchanged between suricata and a program connecting to a dedicated socket. The protocol is the following: * Client connects to the socket * It sends a version message: { "version": "$VERSION_ID" } * Server answers with { "return": "OK|NOK" } If server returns OK, the client is now allowed to send command. The format of command is the following: { "command": "pcap-file", "arguments": { "filename": "smtp-clean.pcap", "output-dir": "/tmp/out" } } The server will try to execute the "command" specified with the (optional) provided "arguments". The answer by server is the following: { "return": "OK|NOK", "message": JSON_OBJECT or information string } A simple script is provided and is available under scripts/suricatasc. It is not intended to be enterprise-grade tool but it is more a proof of concept/example code. The first command line argument of suricatasc is used to specify the socket to connect to. Configuration of the feature is made in the YAML under the 'unix-command' section: unix-command: enabled: yes filename: custom.socket The path specified in 'filename' is not absolute and is relative to the state directory. A new running mode called 'unix-socket' is also added. When starting in this mode, only a unix socket manager is started. When it receives a 'pcap-file' command, the manager start a 'pcap-file' running mode which does not really leave at the end of file but simply exit. The manager is then able to start a new running mode with a new file. To start this mode, Suricata must be started with the --unix-socket option which has an optional argument which fix the file name of the socket. The path is not absolute and is relative to the state directory. THe 'pcap-file' command adds a file to the list of files to treat. For each pcap file, a pcap file running mode is started and the output directory is changed to what specified in the command. The running mode specified in the 'runmode' YAML setting is used to select which running mode must be use for the pcap file treatment. This requires modification in suricata.c file where initialisation code is now conditional to the fact 'unix-socket' mode is not used. Two other commands exists to get info on the remaining tasks: * pcap-file-number: return the number of files in the waiting queue * pcap-file-list: return the list of waiting files 'pcap-file-list' returns a structured object as message. The structure is the following: { 'count': 2, 'files': ['file1.pcap', 'file2.pcap'] }
14 years ago
void TmThreadClearThreadsFamily(int family)
{
ThreadVars *tv = NULL;
ThreadVars *ptv = NULL;
if ((family < 0) || (family >= TVT_MAX))
return;
tv = tv_root[family];
while (tv) {
ptv = tv;
tv = tv->next;
TmThreadFree(ptv);
}
tv_root[family] = NULL;
}
/**
* \brief Spawns a thread associated with the ThreadVars instance tv
*
* \retval TM_ECODE_OK on success and TM_ECODE_FAILED on failure
*/
TmEcode TmThreadSpawn(ThreadVars *tv)
{
pthread_attr_t attr;
if (tv->tm_func == NULL) {
printf("ERROR: no thread function set\n");
return TM_ECODE_FAILED;
}
/* Initialize and set thread detached attribute */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
int rc = pthread_create(&tv->t, &attr, tv->tm_func, (void *)tv);
if (rc) {
printf("ERROR; return code from pthread_create() is %" PRId32 "\n", rc);
return TM_ECODE_FAILED;
}
TmThreadWaitForFlag(tv, THV_INIT_DONE | THV_RUNNING_DONE);
TmThreadAppend(tv, tv->type);
return TM_ECODE_OK;
}
/**
* \brief Sets the thread flags for a thread instance(tv)
*
* \param tv Pointer to the thread instance for which the flag has to be set
* \param flags Holds the thread state this thread instance has to be set to
*/
#if 0
void TmThreadSetFlags(ThreadVars *tv, uint8_t flags)
{
if (tv != NULL)
tv->flags = flags;
return;
}
#endif
/**
* \brief Sets the aof(Action on failure) for a thread instance(tv)
*
* \param tv Pointer to the thread instance for which the aof has to be set
* \param aof Holds the aof this thread instance has to be set to
*/
void TmThreadSetAOF(ThreadVars *tv, uint8_t aof)
{
if (tv != NULL)
tv->aof = aof;
return;
}
/**
* \brief Initializes the mutex and condition variables for this TV
*
* \param tv Pointer to a TV instance
*/
void TmThreadInitMC(ThreadVars *tv)
{
if ( (tv->m = SCMalloc(sizeof(SCMutex))) == NULL) {
SCLogError(SC_ERR_FATAL, "Fatal error encountered in TmThreadInitMC. "
"Exiting...");
exit(EXIT_FAILURE);
}
if (SCMutexInit(tv->m, NULL) != 0) {
printf("Error initializing the tv->m mutex\n");
exit(0);
}
if ( (tv->cond = SCMalloc(sizeof(SCCondT))) == NULL) {
SCLogError(SC_ERR_FATAL, "Fatal error encountered in TmThreadInitMC. "
"Exiting...");
exit(0);
}
if (SCCondInit(tv->cond, NULL) != 0) {
SCLogError(SC_ERR_FATAL, "Error initializing the tv->cond condition "
"variable");
exit(0);
}
return;
}
/**
* \brief Tests if the thread represented in the arg has been unpaused or not.
*
* The function would return if the thread tv has been unpaused or if the
* kill flag for the thread has been set.
*
* \param tv Pointer to the TV instance.
*/
void TmThreadTestThreadUnPaused(ThreadVars *tv)
{
while (TmThreadsCheckFlag(tv, THV_PAUSE)) {
usleep(100);
if (TmThreadsCheckFlag(tv, THV_KILL))
break;
}
return;
}
/**
* \brief Waits till the specified flag(s) is(are) set. We don't bother if
* the kill flag has been set or not on the thread.
*
* \param tv Pointer to the TV instance.
*/
void TmThreadWaitForFlag(ThreadVars *tv, uint16_t flags)
{
while (!TmThreadsCheckFlag(tv, flags)) {
usleep(100);
}
return;
}
/**
* \brief Unpauses a thread
*
* \param tv Pointer to a TV instance that has to be unpaused
*/
void TmThreadContinue(ThreadVars *tv)
{
TmThreadsUnsetFlag(tv, THV_PAUSE);
return;
}
/**
* \brief Unpauses all threads present in tv_root
*/
void TmThreadContinueThreads()
{
ThreadVars *tv = NULL;
int i = 0;
for (i = 0; i < TVT_MAX; i++) {
tv = tv_root[i];
while (tv != NULL) {
TmThreadContinue(tv);
tv = tv->next;
}
}
return;
}
/**
* \brief Pauses a thread
*
* \param tv Pointer to a TV instance that has to be paused
*/
void TmThreadPause(ThreadVars *tv)
{
TmThreadsSetFlag(tv, THV_PAUSE);
return;
}
/**
* \brief Pauses all threads present in tv_root
*/
void TmThreadPauseThreads()
{
ThreadVars *tv = NULL;
int i = 0;
for (i = 0; i < TVT_MAX; i++) {
tv = tv_root[i];
while (tv != NULL) {
TmThreadPause(tv);
tv = tv->next;
}
}
return;
}
/**
* \brief Restarts the thread sent as the argument
*
* \param tv Pointer to the thread instance(tv) to be restarted
*/
static void TmThreadRestartThread(ThreadVars *tv)
{
if (tv->restarted >= THV_MAX_RESTARTS) {
SCLogError(SC_ERR_TM_THREADS_ERROR,"thread restarts exceeded "
"threshold limit for thread \"%s\"", tv->name);
exit(EXIT_FAILURE);
}
TmThreadsUnsetFlag(tv, THV_CLOSED);
TmThreadsUnsetFlag(tv, THV_FAILED);
if (TmThreadSpawn(tv) != TM_ECODE_OK) {
SCLogError(SC_ERR_THREAD_SPAWN, "thread \"%s\" failed to spawn", tv->name);
exit(EXIT_FAILURE);
}
tv->restarted++;
SCLogInfo("thread \"%s\" restarted", tv->name);
return;
}
/**
* \brief Used to check the thread for certain conditions of failure. If the
* thread has been specified to restart on failure, the thread is
* restarted. If the thread has been specified to gracefully shutdown
* the engine on failure, it does so. The global aof flag, tv_aof
* overrides the thread aof flag, if it holds a THV_ENGINE_EXIT;
*/
void TmThreadCheckThreadState(void)
{
ThreadVars *tv = NULL;
int i = 0;
for (i = 0; i < TVT_MAX; i++) {
tv = tv_root[i];
while (tv) {
if (TmThreadsCheckFlag(tv, THV_FAILED)) {
TmThreadsSetFlag(tv, THV_DEINIT);
pthread_join(tv->t, NULL);
if ((tv_aof & THV_ENGINE_EXIT) || (tv->aof & THV_ENGINE_EXIT)) {
EngineKill();
return;
} else {
/* if the engine kill-stop has been received by now, chuck
* restarting and return to kill the engine */
if ((suricata_ctl_flags & SURICATA_KILL) ||
(suricata_ctl_flags & SURICATA_STOP)) {
return;
}
TmThreadRestartThread(tv);
}
}
tv = tv->next;
}
}
return;
}
/**
* \brief Used to check if all threads have finished their initialization. On
* finding an un-initialized thread, it waits till that thread completes
* its initialization, before proceeding to the next thread.
*
* \retval TM_ECODE_OK all initialized properly
* \retval TM_ECODE_FAILED failure
*/
TmEcode TmThreadWaitOnThreadInit(void)
{
ThreadVars *tv = NULL;
int i = 0;
uint16_t mgt_num = 0;
uint16_t ppt_num = 0;
for (i = 0; i < TVT_MAX; i++) {
tv = tv_root[i];
while (tv != NULL) {
char started = FALSE;
while (started == FALSE) {
if (TmThreadsCheckFlag(tv, THV_INIT_DONE)) {
started = TRUE;
} else {
/* sleep a little to give the thread some
* time to finish initialization */
usleep(100);
}
if (TmThreadsCheckFlag(tv, THV_FAILED)) {
SCLogError(SC_ERR_THREAD_INIT, "thread \"%s\" failed to "
"initialize.", tv->name);
return TM_ECODE_FAILED;
}
if (TmThreadsCheckFlag(tv, THV_CLOSED)) {
SCLogError(SC_ERR_THREAD_INIT, "thread \"%s\" closed on "
"initialization.", tv->name);
return TM_ECODE_FAILED;
}
}
if (i == TVT_MGMT) mgt_num++;
else if (i == TVT_PPT) ppt_num++;
tv = tv->next;
}
}
SCLogInfo("all %"PRIu16" packet processing threads, %"PRIu16" management "
"threads initialized, engine started.", ppt_num, mgt_num);
return TM_ECODE_OK;
}
/**
* \brief Returns the TV for the calling thread.
*
* \retval tv Pointer to the ThreadVars instance for the calling thread;
* NULL on no match
*/
ThreadVars *TmThreadsGetCallingThread(void)
{
pthread_t self = pthread_self();
ThreadVars *tv = NULL;
int i = 0;
SCMutexLock(&tv_root_lock);
for (i = 0; i < TVT_MAX; i++) {
tv = tv_root[i];
while (tv) {
if (pthread_equal(self, tv->t)) {
SCMutexUnlock(&tv_root_lock);
return tv;
}
tv = tv->next;
}
}
SCMutexUnlock(&tv_root_lock);
return NULL;
}