mirror of https://github.com/OISF/suricata
Defrag engine
Big rewrite of defrag engine to make it more scalable and fix some locking logic flaws. Now uses a hash of trackers similar to Flow and Host hashes.pull/76/merge
parent
c91c359692
commit
7a044a99ee
@ -0,0 +1,699 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "suricata-common.h"
|
||||||
|
#include "conf.h"
|
||||||
|
#include "defrag-hash.h"
|
||||||
|
#include "defrag-queue.h"
|
||||||
|
#include "util-random.h"
|
||||||
|
#include "util-byte.h"
|
||||||
|
#include "util-misc.h"
|
||||||
|
#include "util-hash-lookup3.h"
|
||||||
|
|
||||||
|
static DefragTracker *DefragTrackerGetUsedDefragTracker(void);
|
||||||
|
|
||||||
|
/** queue with spare tracker */
|
||||||
|
static DefragTrackerQueue defragtracker_spare_q;
|
||||||
|
|
||||||
|
uint32_t DefragTrackerSpareQueueGetSize(void) {
|
||||||
|
return DefragTrackerQueueLen(&defragtracker_spare_q);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefragTrackerMoveToSpare(DefragTracker *h) {
|
||||||
|
DefragTrackerEnqueue(&defragtracker_spare_q, h);
|
||||||
|
(void) SC_ATOMIC_SUB(defragtracker_counter, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefragTracker *DefragTrackerAlloc(void) {
|
||||||
|
if (!(DEFRAG_CHECK_MEMCAP(sizeof(DefragTracker)))) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) SC_ATOMIC_ADD(defrag_memuse, sizeof(DefragTracker));
|
||||||
|
|
||||||
|
DefragTracker *dt = SCMalloc(sizeof(DefragTracker));
|
||||||
|
if (dt == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
memset(dt, 0x00, sizeof(DefragTracker));
|
||||||
|
|
||||||
|
SCMutexInit(&dt->lock, NULL);
|
||||||
|
SC_ATOMIC_INIT(dt->use_cnt);
|
||||||
|
return dt;
|
||||||
|
|
||||||
|
error:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefragTrackerFree(DefragTracker *dt) {
|
||||||
|
if (dt != NULL) {
|
||||||
|
DefragTrackerClearMemory(dt);
|
||||||
|
|
||||||
|
SCMutexDestroy(&dt->lock);
|
||||||
|
SCFree(dt);
|
||||||
|
(void) SC_ATOMIC_SUB(defrag_memuse, sizeof(DefragTracker));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DefragTrackerIncrUsecnt(dt) \
|
||||||
|
SC_ATOMIC_ADD((dt)->use_cnt, 1)
|
||||||
|
#define DefragTrackerDecrUsecnt(dt) \
|
||||||
|
SC_ATOMIC_SUB((dt)->use_cnt, 1)
|
||||||
|
|
||||||
|
static void DefragTrackerInit(DefragTracker *dt, Packet *p) {
|
||||||
|
/* copy address */
|
||||||
|
COPY_ADDRESS(&p->src, &dt->src_addr);
|
||||||
|
COPY_ADDRESS(&p->dst, &dt->dst_addr);
|
||||||
|
|
||||||
|
if (PKT_IS_IPV4(p)) {
|
||||||
|
dt->id = (int32_t)IPV4_GET_IPID(p);
|
||||||
|
dt->af = AF_INET;
|
||||||
|
} else {
|
||||||
|
dt->id = (int32_t)IPV6_EXTHDR_GET_FH_ID(p);
|
||||||
|
dt->af = AF_INET6;
|
||||||
|
}
|
||||||
|
dt->policy = DefragGetOsPolicy(p);
|
||||||
|
TAILQ_INIT(&dt->frags);
|
||||||
|
(void) DefragTrackerIncrUsecnt(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DefragTracker *DefragTrackerNew(Packet *p) {
|
||||||
|
DefragTracker *dt = DefragTrackerAlloc();
|
||||||
|
if (dt == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
DefragTrackerInit(dt, p);
|
||||||
|
return dt;
|
||||||
|
|
||||||
|
error:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefragTrackerRelease(DefragTracker *t) {
|
||||||
|
(void) DefragTrackerDecrUsecnt(t);
|
||||||
|
SCMutexUnlock(&t->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefragTrackerClearMemory(DefragTracker *dt) {
|
||||||
|
DefragTrackerFreeFrags(dt);
|
||||||
|
SC_ATOMIC_DESTROY(dt->use_cnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEFRAG_DEFAULT_HASHSIZE 4096
|
||||||
|
#define DEFRAG_DEFAULT_MEMCAP 16777216
|
||||||
|
#define DEFRAG_DEFAULT_PREALLOC 1000
|
||||||
|
|
||||||
|
/** \brief initialize the configuration
|
||||||
|
* \warning Not thread safe */
|
||||||
|
void DefragInitConfig(char quiet)
|
||||||
|
{
|
||||||
|
SCLogDebug("initializing defrag engine...");
|
||||||
|
|
||||||
|
memset(&defrag_config, 0, sizeof(defrag_config));
|
||||||
|
//SC_ATOMIC_INIT(flow_flags);
|
||||||
|
SC_ATOMIC_INIT(defragtracker_counter);
|
||||||
|
SC_ATOMIC_INIT(defrag_memuse);
|
||||||
|
SC_ATOMIC_INIT(defragtracker_prune_idx);
|
||||||
|
DefragTrackerQueueInit(&defragtracker_spare_q);
|
||||||
|
|
||||||
|
unsigned int seed = RandomTimePreseed();
|
||||||
|
/* set defaults */
|
||||||
|
defrag_config.hash_rand = (int)(DEFRAG_DEFAULT_HASHSIZE * (rand_r(&seed) / RAND_MAX + 1.0));
|
||||||
|
|
||||||
|
defrag_config.hash_size = DEFRAG_DEFAULT_HASHSIZE;
|
||||||
|
defrag_config.memcap = DEFRAG_DEFAULT_MEMCAP;
|
||||||
|
defrag_config.prealloc = DEFRAG_DEFAULT_PREALLOC;
|
||||||
|
|
||||||
|
/* Check if we have memcap and hash_size defined at config */
|
||||||
|
char *conf_val;
|
||||||
|
uint32_t configval = 0;
|
||||||
|
|
||||||
|
/** set config values for memcap, prealloc and hash_size */
|
||||||
|
if ((ConfGet("defrag.memcap", &conf_val)) == 1)
|
||||||
|
{
|
||||||
|
if (ParseSizeStringU64(conf_val, &defrag_config.memcap) < 0) {
|
||||||
|
SCLogError(SC_ERR_SIZE_PARSE, "Error parsing defrag.memcap "
|
||||||
|
"from conf file - %s. Killing engine",
|
||||||
|
conf_val);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((ConfGet("defrag.hash-size", &conf_val)) == 1)
|
||||||
|
{
|
||||||
|
if (ByteExtractStringUint32(&configval, 10, strlen(conf_val),
|
||||||
|
conf_val) > 0) {
|
||||||
|
defrag_config.hash_size = configval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ((ConfGet("defrag.trackers", &conf_val)) == 1)
|
||||||
|
{
|
||||||
|
if (ByteExtractStringUint32(&configval, 10, strlen(conf_val),
|
||||||
|
conf_val) > 0) {
|
||||||
|
defrag_config.prealloc = configval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SCLogDebug("DefragTracker config from suricata.yaml: memcap: %"PRIu64", hash-size: "
|
||||||
|
"%"PRIu32", prealloc: %"PRIu32, defrag_config.memcap,
|
||||||
|
defrag_config.hash_size, defrag_config.prealloc);
|
||||||
|
|
||||||
|
/* alloc hash memory */
|
||||||
|
uint64_t hash_size = defrag_config.hash_size * sizeof(DefragTrackerHashRow);
|
||||||
|
if (!(DEFRAG_CHECK_MEMCAP(hash_size))) {
|
||||||
|
SCLogError(SC_ERR_DEFRAG_INIT, "allocating defrag hash failed: "
|
||||||
|
"max defrag memcap is smaller than projected hash size. "
|
||||||
|
"Memcap: %"PRIu64", Hash table size %"PRIu64". Calculate "
|
||||||
|
"total hash size by multiplying \"defrag.hash-size\" with %"PRIuMAX", "
|
||||||
|
"which is the hash bucket size.", defrag_config.memcap, hash_size,
|
||||||
|
(uintmax_t)sizeof(DefragTrackerHashRow));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
defragtracker_hash = SCCalloc(defrag_config.hash_size, sizeof(DefragTrackerHashRow));
|
||||||
|
if (defragtracker_hash == NULL) {
|
||||||
|
SCLogError(SC_ERR_FATAL, "Fatal error encountered in DefragTrackerInitConfig. Exiting...");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
memset(defragtracker_hash, 0, defrag_config.hash_size * sizeof(DefragTrackerHashRow));
|
||||||
|
|
||||||
|
uint32_t i = 0;
|
||||||
|
for (i = 0; i < defrag_config.hash_size; i++) {
|
||||||
|
DRLOCK_INIT(&defragtracker_hash[i]);
|
||||||
|
}
|
||||||
|
(void) SC_ATOMIC_ADD(defrag_memuse, (defrag_config.hash_size * sizeof(DefragTrackerHashRow)));
|
||||||
|
|
||||||
|
if (quiet == FALSE) {
|
||||||
|
SCLogInfo("allocated %llu bytes of memory for the defrag hash... "
|
||||||
|
"%" PRIu32 " buckets of size %" PRIuMAX "",
|
||||||
|
SC_ATOMIC_GET(defrag_memuse), defrag_config.hash_size,
|
||||||
|
(uintmax_t)sizeof(DefragTrackerHashRow));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ConfGet("defrag.prealloc", &conf_val)) == 1)
|
||||||
|
{
|
||||||
|
if (ConfValIsTrue(conf_val)) {
|
||||||
|
/* pre allocate defrag trackers */
|
||||||
|
for (i = 0; i < defrag_config.prealloc; i++) {
|
||||||
|
if (!(DEFRAG_CHECK_MEMCAP(sizeof(DefragTracker)))) {
|
||||||
|
SCLogError(SC_ERR_DEFRAG_INIT, "preallocating defrag trackers failed: "
|
||||||
|
"max defrag memcap reached. Memcap %"PRIu64", "
|
||||||
|
"Memuse %"PRIu64".", defrag_config.memcap,
|
||||||
|
((uint64_t)SC_ATOMIC_GET(defrag_memuse) + (uint64_t)sizeof(DefragTracker)));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefragTracker *h = DefragTrackerAlloc();
|
||||||
|
if (h == NULL) {
|
||||||
|
SCLogError(SC_ERR_DEFRAG_INIT, "preallocating defrag failed: %s", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
DefragTrackerEnqueue(&defragtracker_spare_q,h);
|
||||||
|
}
|
||||||
|
if (quiet == FALSE) {
|
||||||
|
SCLogInfo("preallocated %" PRIu32 " defrag trackers of size %" PRIuMAX "",
|
||||||
|
defragtracker_spare_q.len, (uintmax_t)sizeof(DefragTracker));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quiet == FALSE) {
|
||||||
|
SCLogInfo("defrag memory usage: %llu bytes, maximum: %"PRIu64,
|
||||||
|
SC_ATOMIC_GET(defrag_memuse), defrag_config.memcap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief print some defrag stats
|
||||||
|
* \warning Not thread safe */
|
||||||
|
static void DefragTrackerPrintStats (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief shutdown the flow engine
|
||||||
|
* \warning Not thread safe */
|
||||||
|
void DefragHashShutdown(void)
|
||||||
|
{
|
||||||
|
DefragTracker *dt;
|
||||||
|
uint32_t u;
|
||||||
|
|
||||||
|
DefragTrackerPrintStats();
|
||||||
|
|
||||||
|
/* free spare queue */
|
||||||
|
while((dt = DefragTrackerDequeue(&defragtracker_spare_q))) {
|
||||||
|
BUG_ON(SC_ATOMIC_GET(dt->use_cnt) > 0);
|
||||||
|
DefragTrackerFree(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear and free the hash */
|
||||||
|
if (defragtracker_hash != NULL) {
|
||||||
|
for (u = 0; u < defrag_config.hash_size; u++) {
|
||||||
|
dt = defragtracker_hash[u].head;
|
||||||
|
while (dt) {
|
||||||
|
DefragTracker *n = dt->hnext;
|
||||||
|
DefragTrackerClearMemory(dt);
|
||||||
|
DefragTrackerFree(dt);
|
||||||
|
dt = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
DRLOCK_DESTROY(&defragtracker_hash[u]);
|
||||||
|
}
|
||||||
|
SCFree(defragtracker_hash);
|
||||||
|
defragtracker_hash = NULL;
|
||||||
|
}
|
||||||
|
(void) SC_ATOMIC_SUB(defrag_memuse, defrag_config.hash_size * sizeof(DefragTrackerHashRow));
|
||||||
|
DefragTrackerQueueDestroy(&defragtracker_spare_q);
|
||||||
|
|
||||||
|
SC_ATOMIC_DESTROY(defragtracker_prune_idx);
|
||||||
|
SC_ATOMIC_DESTROY(defrag_memuse);
|
||||||
|
SC_ATOMIC_DESTROY(defragtracker_counter);
|
||||||
|
//SC_ATOMIC_DESTROY(flow_flags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief compare two raw ipv6 addrs
|
||||||
|
*
|
||||||
|
* \note we don't care about the real ipv6 ip's, this is just
|
||||||
|
* to consistently fill the DefragHashKey6 struct, without all
|
||||||
|
* the ntohl calls.
|
||||||
|
*
|
||||||
|
* \warning do not use elsewhere unless you know what you're doing.
|
||||||
|
* detect-engine-address-ipv6.c's AddressIPv6GtU32 is likely
|
||||||
|
* what you are looking for.
|
||||||
|
*/
|
||||||
|
static inline int DefragHashRawAddressIPv6GtU32(uint32_t *a, uint32_t *b)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
if (a[i] > b[i])
|
||||||
|
return 1;
|
||||||
|
if (a[i] < b[i])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct DefragHashKey4_ {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint32_t src, dst;
|
||||||
|
uint32_t id;
|
||||||
|
};
|
||||||
|
uint32_t u32[3];
|
||||||
|
};
|
||||||
|
} DefragHashKey4;
|
||||||
|
|
||||||
|
typedef struct DefragHashKey6_ {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint32_t src[4], dst[4];
|
||||||
|
uint32_t id;
|
||||||
|
};
|
||||||
|
uint32_t u32[9];
|
||||||
|
};
|
||||||
|
} DefragHashKey6;
|
||||||
|
|
||||||
|
/* calculate the hash key for this packet
|
||||||
|
*
|
||||||
|
* we're using:
|
||||||
|
* hash_rand -- set at init time
|
||||||
|
* source address
|
||||||
|
* destination address
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
static inline uint32_t DefragHashGetKey(Packet *p) {
|
||||||
|
uint32_t key;
|
||||||
|
|
||||||
|
if (p->ip4h != NULL) {
|
||||||
|
DefragHashKey4 dhk;
|
||||||
|
if (p->src.addr_data32[0] > p->dst.addr_data32[0]) {
|
||||||
|
dhk.src = p->src.addr_data32[0];
|
||||||
|
dhk.dst = p->dst.addr_data32[0];
|
||||||
|
} else {
|
||||||
|
dhk.src = p->dst.addr_data32[0];
|
||||||
|
dhk.dst = p->src.addr_data32[0];
|
||||||
|
}
|
||||||
|
dhk.id = (uint32_t)IPV4_GET_IPID(p);
|
||||||
|
|
||||||
|
uint32_t hash = hashword(dhk.u32, 3, defrag_config.hash_rand);
|
||||||
|
key = hash % defrag_config.hash_size;
|
||||||
|
} else if (p->ip6h != NULL) {
|
||||||
|
DefragHashKey6 dhk;
|
||||||
|
if (DefragHashRawAddressIPv6GtU32(p->src.addr_data32, p->dst.addr_data32)) {
|
||||||
|
dhk.src[0] = p->src.addr_data32[0];
|
||||||
|
dhk.src[1] = p->src.addr_data32[1];
|
||||||
|
dhk.src[2] = p->src.addr_data32[2];
|
||||||
|
dhk.src[3] = p->src.addr_data32[3];
|
||||||
|
dhk.dst[0] = p->dst.addr_data32[0];
|
||||||
|
dhk.dst[1] = p->dst.addr_data32[1];
|
||||||
|
dhk.dst[2] = p->dst.addr_data32[2];
|
||||||
|
dhk.dst[3] = p->dst.addr_data32[3];
|
||||||
|
} else {
|
||||||
|
dhk.src[0] = p->dst.addr_data32[0];
|
||||||
|
dhk.src[1] = p->dst.addr_data32[1];
|
||||||
|
dhk.src[2] = p->dst.addr_data32[2];
|
||||||
|
dhk.src[3] = p->dst.addr_data32[3];
|
||||||
|
dhk.dst[0] = p->src.addr_data32[0];
|
||||||
|
dhk.dst[1] = p->src.addr_data32[1];
|
||||||
|
dhk.dst[2] = p->src.addr_data32[2];
|
||||||
|
dhk.dst[3] = p->src.addr_data32[3];
|
||||||
|
}
|
||||||
|
dhk.id = IPV6_EXTHDR_GET_FH_ID(p);
|
||||||
|
|
||||||
|
uint32_t hash = hashword(dhk.u32, 9, defrag_config.hash_rand);
|
||||||
|
key = hash % defrag_config.hash_size;
|
||||||
|
} else
|
||||||
|
key = 0;
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Since two or more trackers can have the same hash key, we need to compare
|
||||||
|
* the tracker with the current tracker key. */
|
||||||
|
#define CMP_DEFRAGTRACKER(d1,d2,id) \
|
||||||
|
(((CMP_ADDR(&(d1)->src_addr, &(d2)->src) && \
|
||||||
|
CMP_ADDR(&(d1)->dst_addr, &(d2)->dst)) || \
|
||||||
|
(CMP_ADDR(&(d1)->src_addr, &(d2)->dst) && \
|
||||||
|
CMP_ADDR(&(d1)->dst_addr, &(d2)->src))) && \
|
||||||
|
(d1)->id == (id))
|
||||||
|
|
||||||
|
static inline int DefragTrackerCompare(DefragTracker *t, Packet *p) {
|
||||||
|
uint32_t id;
|
||||||
|
if (PKT_IS_IPV4(p)) {
|
||||||
|
id = (uint32_t)IPV4_GET_IPID(p);
|
||||||
|
} else {
|
||||||
|
id = IPV6_EXTHDR_GET_FH_ID(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CMP_DEFRAGTRACKER(t, p, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get a new defrag tracker
|
||||||
|
*
|
||||||
|
* Get a new defrag tracker. We're checking memcap first and will try to make room
|
||||||
|
* if the memcap is reached.
|
||||||
|
*
|
||||||
|
* \retval dt *LOCKED* tracker on succes, NULL on error.
|
||||||
|
*/
|
||||||
|
static DefragTracker *DefragTrackerGetNew(Packet *p) {
|
||||||
|
DefragTracker *dt = NULL;
|
||||||
|
|
||||||
|
/* get a tracker from the spare queue */
|
||||||
|
dt = DefragTrackerDequeue(&defragtracker_spare_q);
|
||||||
|
if (dt == NULL) {
|
||||||
|
/* If we reached the max memcap, we get a used tracker */
|
||||||
|
if (!(DEFRAG_CHECK_MEMCAP(sizeof(DefragTracker)))) {
|
||||||
|
/* declare state of emergency */
|
||||||
|
//if (!(SC_ATOMIC_GET(defragtracker_flags) & DEFRAG_EMERGENCY)) {
|
||||||
|
// SC_ATOMIC_OR(defragtracker_flags, DEFRAG_EMERGENCY);
|
||||||
|
|
||||||
|
/* under high load, waking up the flow mgr each time leads
|
||||||
|
* to high cpu usage. Flows are not timed out much faster if
|
||||||
|
* we check a 1000 times a second. */
|
||||||
|
// FlowWakeupFlowManagerThread();
|
||||||
|
//}
|
||||||
|
|
||||||
|
dt = DefragTrackerGetUsedDefragTracker();
|
||||||
|
if (dt == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* freed a tracker, but it's unlocked */
|
||||||
|
} else {
|
||||||
|
/* now see if we can alloc a new tracker */
|
||||||
|
dt = DefragTrackerNew(p);
|
||||||
|
if (dt == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tracker is initialized but *unlocked* */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* tracker has been recycled before it went into the spare queue */
|
||||||
|
|
||||||
|
/* tracker is initialized (recylced) but *unlocked* */
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) SC_ATOMIC_ADD(defragtracker_counter, 1);
|
||||||
|
SCMutexLock(&dt->lock);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DefragGetTrackerFromHash
|
||||||
|
*
|
||||||
|
* Hash retrieval function for trackers. Looks up the hash bucket containing the
|
||||||
|
* tracker pointer. Then compares the packet with the found tracker to see if it is
|
||||||
|
* the tracker we need. If it isn't, walk the list until the right tracker is found.
|
||||||
|
*
|
||||||
|
* returns a *LOCKED* tracker or NULL
|
||||||
|
*/
|
||||||
|
DefragTracker *DefragGetTrackerFromHash (Packet *p)
|
||||||
|
{
|
||||||
|
DefragTracker *dt = NULL;
|
||||||
|
|
||||||
|
/* get the key to our bucket */
|
||||||
|
uint32_t key = DefragHashGetKey(p);
|
||||||
|
/* get our hash bucket and lock it */
|
||||||
|
DefragTrackerHashRow *hb = &defragtracker_hash[key];
|
||||||
|
DRLOCK_LOCK(hb);
|
||||||
|
|
||||||
|
/* see if the bucket already has a tracker */
|
||||||
|
if (hb->head == NULL) {
|
||||||
|
dt = DefragTrackerGetNew(p);
|
||||||
|
if (dt == NULL) {
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tracker is locked */
|
||||||
|
hb->head = dt;
|
||||||
|
hb->tail = dt;
|
||||||
|
|
||||||
|
/* got one, now lock, initialize and return */
|
||||||
|
DefragTrackerInit(dt,p);
|
||||||
|
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ok, we have a tracker in the bucket. Let's find out if it is our tracker */
|
||||||
|
dt = hb->head;
|
||||||
|
|
||||||
|
/* see if this is the tracker we are looking for */
|
||||||
|
if (DefragTrackerCompare(dt, p) == 0) {
|
||||||
|
DefragTracker *pdt = NULL; /* previous tracker */
|
||||||
|
|
||||||
|
while (dt) {
|
||||||
|
pdt = dt;
|
||||||
|
dt = dt->hnext;
|
||||||
|
|
||||||
|
if (dt == NULL) {
|
||||||
|
dt = pdt->hnext = DefragTrackerGetNew(p);
|
||||||
|
if (dt == NULL) {
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
hb->tail = dt;
|
||||||
|
|
||||||
|
/* tracker is locked */
|
||||||
|
|
||||||
|
dt->hprev = pdt;
|
||||||
|
|
||||||
|
/* initialize and return */
|
||||||
|
DefragTrackerInit(dt,p);
|
||||||
|
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DefragTrackerCompare(dt, p) != 0) {
|
||||||
|
/* we found our tracker, lets put it on top of the
|
||||||
|
* hash list -- this rewards active trackers */
|
||||||
|
if (dt->hnext) {
|
||||||
|
dt->hnext->hprev = dt->hprev;
|
||||||
|
}
|
||||||
|
if (dt->hprev) {
|
||||||
|
dt->hprev->hnext = dt->hnext;
|
||||||
|
}
|
||||||
|
if (dt == hb->tail) {
|
||||||
|
hb->tail = dt->hprev;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt->hnext = hb->head;
|
||||||
|
dt->hprev = NULL;
|
||||||
|
hb->head->hprev = dt;
|
||||||
|
hb->head = dt;
|
||||||
|
|
||||||
|
/* found our tracker, lock & return */
|
||||||
|
SCMutexLock(&dt->lock);
|
||||||
|
(void) DefragTrackerIncrUsecnt(dt);
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lock & return */
|
||||||
|
SCMutexLock(&dt->lock);
|
||||||
|
(void) DefragTrackerIncrUsecnt(dt);
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief look up a tracker in the hash
|
||||||
|
*
|
||||||
|
* \param a address to look up
|
||||||
|
*
|
||||||
|
* \retval h *LOCKED* tracker or NULL
|
||||||
|
*/
|
||||||
|
DefragTracker *DefragLookupTrackerFromHash (Packet *p)
|
||||||
|
{
|
||||||
|
DefragTracker *dt = NULL;
|
||||||
|
|
||||||
|
/* get the key to our bucket */
|
||||||
|
uint32_t key = DefragHashGetKey(p);
|
||||||
|
/* get our hash bucket and lock it */
|
||||||
|
DefragTrackerHashRow *hb = &defragtracker_hash[key];
|
||||||
|
DRLOCK_LOCK(hb);
|
||||||
|
|
||||||
|
/* see if the bucket already has a tracker */
|
||||||
|
if (hb->head == NULL) {
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ok, we have a tracker in the bucket. Let's find out if it is our tracker */
|
||||||
|
dt = hb->head;
|
||||||
|
|
||||||
|
/* see if this is the tracker we are looking for */
|
||||||
|
if (DefragTrackerCompare(dt, p) == 0) {
|
||||||
|
while (dt) {
|
||||||
|
dt = dt->hnext;
|
||||||
|
|
||||||
|
if (dt == NULL) {
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DefragTrackerCompare(dt, p) != 0) {
|
||||||
|
/* we found our tracker, lets put it on top of the
|
||||||
|
* hash list -- this rewards active tracker */
|
||||||
|
if (dt->hnext) {
|
||||||
|
dt->hnext->hprev = dt->hprev;
|
||||||
|
}
|
||||||
|
if (dt->hprev) {
|
||||||
|
dt->hprev->hnext = dt->hnext;
|
||||||
|
}
|
||||||
|
if (dt == hb->tail) {
|
||||||
|
hb->tail = dt->hprev;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt->hnext = hb->head;
|
||||||
|
dt->hprev = NULL;
|
||||||
|
hb->head->hprev = dt;
|
||||||
|
hb->head = dt;
|
||||||
|
|
||||||
|
/* found our tracker, lock & return */
|
||||||
|
SCMutexLock(&dt->lock);
|
||||||
|
(void) DefragTrackerIncrUsecnt(dt);
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lock & return */
|
||||||
|
SCMutexLock(&dt->lock);
|
||||||
|
(void) DefragTrackerIncrUsecnt(dt);
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \internal
|
||||||
|
* \brief Get a tracker from the hash directly.
|
||||||
|
*
|
||||||
|
* Called in conditions where the spare queue is empty and memcap is reached.
|
||||||
|
*
|
||||||
|
* Walks the hash until a tracker can be freed. "defragtracker_prune_idx" atomic int makes
|
||||||
|
* sure we don't start at the top each time since that would clear the top of
|
||||||
|
* the hash leading to longer and longer search times under high pressure (observed).
|
||||||
|
*
|
||||||
|
* \retval dt tracker or NULL
|
||||||
|
*/
|
||||||
|
static DefragTracker *DefragTrackerGetUsedDefragTracker(void) {
|
||||||
|
uint32_t idx = SC_ATOMIC_GET(defragtracker_prune_idx) % defrag_config.hash_size;
|
||||||
|
uint32_t cnt = defrag_config.hash_size;
|
||||||
|
|
||||||
|
while (cnt--) {
|
||||||
|
if (idx++ >= defrag_config.hash_size)
|
||||||
|
idx = 0;
|
||||||
|
|
||||||
|
DefragTrackerHashRow *hb = &defragtracker_hash[idx];
|
||||||
|
if (hb == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (DRLOCK_TRYLOCK(hb) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DefragTracker *dt = hb->tail;
|
||||||
|
if (dt == NULL) {
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SCMutexTrylock(&dt->lock) != 0) {
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** never prune a tracker that is used by a packets
|
||||||
|
* we are currently processing in one of the threads */
|
||||||
|
if (SC_ATOMIC_GET(dt->use_cnt) > 0) {
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
SCMutexUnlock(&dt->lock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove from the hash */
|
||||||
|
if (dt->hprev != NULL)
|
||||||
|
dt->hprev->hnext = dt->hnext;
|
||||||
|
if (dt->hnext != NULL)
|
||||||
|
dt->hnext->hprev = dt->hprev;
|
||||||
|
if (hb->head == dt)
|
||||||
|
hb->head = dt->hnext;
|
||||||
|
if (hb->tail == dt)
|
||||||
|
hb->tail = dt->hprev;
|
||||||
|
|
||||||
|
dt->hnext = NULL;
|
||||||
|
dt->hprev = NULL;
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
|
||||||
|
DefragTrackerClearMemory(dt);
|
||||||
|
|
||||||
|
SCMutexUnlock(&dt->lock);
|
||||||
|
|
||||||
|
(void) SC_ATOMIC_ADD(defragtracker_prune_idx, (defrag_config.hash_size - cnt));
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
|||||||
|
/* 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>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DEFRAG_HASH_H__
|
||||||
|
#define __DEFRAG_HASH_H__
|
||||||
|
|
||||||
|
#include "decode.h"
|
||||||
|
#include "defrag.h"
|
||||||
|
|
||||||
|
/** Spinlocks or Mutex for the flow buckets. */
|
||||||
|
//#define DRLOCK_SPIN
|
||||||
|
#define DRLOCK_MUTEX
|
||||||
|
|
||||||
|
#ifdef DRLOCK_SPIN
|
||||||
|
#ifdef DRLOCK_MUTEX
|
||||||
|
#error Cannot enable both DRLOCK_SPIN and DRLOCK_MUTEX
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DRLOCK_SPIN
|
||||||
|
#define DRLOCK_TYPE SCSpinlock
|
||||||
|
#define DRLOCK_INIT(fb) SCSpinInit(&(fb)->lock, 0)
|
||||||
|
#define DRLOCK_DESTROY(fb) SCSpinDestroy(&(fb)->lock)
|
||||||
|
#define DRLOCK_LOCK(fb) SCSpinLock(&(fb)->lock)
|
||||||
|
#define DRLOCK_TRYLOCK(fb) SCSpinTrylock(&(fb)->lock)
|
||||||
|
#define DRLOCK_UNLOCK(fb) SCSpinUnlock(&(fb)->lock)
|
||||||
|
#elif defined DRLOCK_MUTEX
|
||||||
|
#define DRLOCK_TYPE SCMutex
|
||||||
|
#define DRLOCK_INIT(fb) SCMutexInit(&(fb)->lock, NULL)
|
||||||
|
#define DRLOCK_DESTROY(fb) SCMutexDestroy(&(fb)->lock)
|
||||||
|
#define DRLOCK_LOCK(fb) SCMutexLock(&(fb)->lock)
|
||||||
|
#define DRLOCK_TRYLOCK(fb) SCMutexTrylock(&(fb)->lock)
|
||||||
|
#define DRLOCK_UNLOCK(fb) SCMutexUnlock(&(fb)->lock)
|
||||||
|
#else
|
||||||
|
#error Enable DRLOCK_SPIN or DRLOCK_MUTEX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct DefragTrackerHashRow_ {
|
||||||
|
DRLOCK_TYPE lock;
|
||||||
|
DefragTracker *head;
|
||||||
|
DefragTracker *tail;
|
||||||
|
} DefragTrackerHashRow;
|
||||||
|
|
||||||
|
/** defrag tracker hash table */
|
||||||
|
DefragTrackerHashRow *defragtracker_hash;
|
||||||
|
|
||||||
|
#define DEFRAG_VERBOSE 0
|
||||||
|
#define DEFRAG_QUIET 1
|
||||||
|
|
||||||
|
typedef struct DefragConfig_ {
|
||||||
|
uint64_t memcap;
|
||||||
|
uint32_t hash_rand;
|
||||||
|
uint32_t hash_size;
|
||||||
|
uint32_t prealloc;
|
||||||
|
} DefragConfig;
|
||||||
|
|
||||||
|
/** \brief check if a memory alloc would fit in the memcap
|
||||||
|
*
|
||||||
|
* \param size memory allocation size to check
|
||||||
|
*
|
||||||
|
* \retval 1 it fits
|
||||||
|
* \retval 0 no fit
|
||||||
|
*/
|
||||||
|
#define DEFRAG_CHECK_MEMCAP(size) \
|
||||||
|
((((uint64_t)SC_ATOMIC_GET(defrag_memuse) + (uint64_t)(size)) <= defrag_config.memcap))
|
||||||
|
|
||||||
|
DefragConfig defrag_config;
|
||||||
|
SC_ATOMIC_DECLARE(unsigned long long int,defrag_memuse);
|
||||||
|
SC_ATOMIC_DECLARE(unsigned int,defragtracker_counter);
|
||||||
|
SC_ATOMIC_DECLARE(unsigned int,defragtracker_prune_idx);
|
||||||
|
|
||||||
|
void DefragInitConfig(char quiet);
|
||||||
|
void DefragHashShutdown(void);
|
||||||
|
|
||||||
|
DefragTracker *DefragLookupTrackerFromHash (Packet *);
|
||||||
|
DefragTracker *DefragGetTrackerFromHash (Packet *);
|
||||||
|
void DefragTrackerRelease(DefragTracker *);
|
||||||
|
void DefragTrackerClearMemory(DefragTracker *);
|
||||||
|
void DefragTrackerMoveToSpare(DefragTracker *);
|
||||||
|
uint32_t DefragTrackerSpareQueueGetSize(void);
|
||||||
|
|
||||||
|
#endif /* __DEFRAG_HASH_H__ */
|
||||||
|
|
@ -0,0 +1,138 @@
|
|||||||
|
/* 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>
|
||||||
|
*
|
||||||
|
* Defrag tracker queue handler functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "suricata-common.h"
|
||||||
|
#include "threads.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "defrag-queue.h"
|
||||||
|
#include "util-error.h"
|
||||||
|
#include "util-debug.h"
|
||||||
|
#include "util-print.h"
|
||||||
|
|
||||||
|
DefragTrackerQueue *DefragTrackerQueueInit (DefragTrackerQueue *q) {
|
||||||
|
if (q != NULL) {
|
||||||
|
memset(q, 0, sizeof(DefragTrackerQueue));
|
||||||
|
DQLOCK_INIT(q);
|
||||||
|
}
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefragTrackerQueue *DefragTrackerQueueNew() {
|
||||||
|
DefragTrackerQueue *q = (DefragTrackerQueue *)SCMalloc(sizeof(DefragTrackerQueue));
|
||||||
|
if (q == NULL) {
|
||||||
|
SCLogError(SC_ERR_FATAL, "Fatal error encountered in DefragTrackerQueueNew. Exiting...");
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
q = DefragTrackerQueueInit(q);
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Destroy a tracker queue
|
||||||
|
*
|
||||||
|
* \param q the tracker queue to destroy
|
||||||
|
*/
|
||||||
|
void DefragTrackerQueueDestroy (DefragTrackerQueue *q) {
|
||||||
|
DQLOCK_DESTROY(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief add a tracker to a queue
|
||||||
|
*
|
||||||
|
* \param q queue
|
||||||
|
* \param dt tracker
|
||||||
|
*/
|
||||||
|
void DefragTrackerEnqueue (DefragTrackerQueue *q, DefragTracker *dt) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
BUG_ON(q == NULL || dt == NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DQLOCK_LOCK(q);
|
||||||
|
|
||||||
|
/* more trackers in queue */
|
||||||
|
if (q->top != NULL) {
|
||||||
|
dt->lnext = q->top;
|
||||||
|
q->top->lprev = dt;
|
||||||
|
q->top = dt;
|
||||||
|
/* only tracker */
|
||||||
|
} else {
|
||||||
|
q->top = dt;
|
||||||
|
q->bot = dt;
|
||||||
|
}
|
||||||
|
q->len++;
|
||||||
|
#ifdef DBG_PERF
|
||||||
|
if (q->len > q->dbg_maxlen)
|
||||||
|
q->dbg_maxlen = q->len;
|
||||||
|
#endif /* DBG_PERF */
|
||||||
|
DQLOCK_UNLOCK(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief remove a tracker from the queue
|
||||||
|
*
|
||||||
|
* \param q queue
|
||||||
|
*
|
||||||
|
* \retval dt tracker or NULL if empty list.
|
||||||
|
*/
|
||||||
|
DefragTracker *DefragTrackerDequeue (DefragTrackerQueue *q) {
|
||||||
|
DQLOCK_LOCK(q);
|
||||||
|
|
||||||
|
DefragTracker *dt = q->bot;
|
||||||
|
if (dt == NULL) {
|
||||||
|
DQLOCK_UNLOCK(q);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* more packets in queue */
|
||||||
|
if (q->bot->lprev != NULL) {
|
||||||
|
q->bot = q->bot->lprev;
|
||||||
|
q->bot->lnext = NULL;
|
||||||
|
/* just the one we remove, so now empty */
|
||||||
|
} else {
|
||||||
|
q->top = NULL;
|
||||||
|
q->bot = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
BUG_ON(q->len == 0);
|
||||||
|
#endif
|
||||||
|
if (q->len > 0)
|
||||||
|
q->len--;
|
||||||
|
|
||||||
|
dt->lnext = NULL;
|
||||||
|
dt->lprev = NULL;
|
||||||
|
|
||||||
|
DQLOCK_UNLOCK(q);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t DefragTrackerQueueLen(DefragTrackerQueue *q) {
|
||||||
|
uint32_t len;
|
||||||
|
DQLOCK_LOCK(q);
|
||||||
|
len = q->len;
|
||||||
|
DQLOCK_UNLOCK(q);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
|||||||
|
/* 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>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DEFRAG_QUEUE_H__
|
||||||
|
#define __DEFRAG_QUEUE_H__
|
||||||
|
|
||||||
|
#include "suricata-common.h"
|
||||||
|
#include "defrag.h"
|
||||||
|
|
||||||
|
/** Spinlocks or Mutex for the defrag tracker queues. */
|
||||||
|
//#define DQLOCK_SPIN
|
||||||
|
#define DQLOCK_MUTEX
|
||||||
|
|
||||||
|
#ifdef DQLOCK_SPIN
|
||||||
|
#ifdef DQLOCK_MUTEX
|
||||||
|
#error Cannot enable both DQLOCK_SPIN and DQLOCK_MUTEX
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Define a queue for storing defrag trackers */
|
||||||
|
typedef struct DefragTrackerQueue_
|
||||||
|
{
|
||||||
|
DefragTracker *top;
|
||||||
|
DefragTracker *bot;
|
||||||
|
uint32_t len;
|
||||||
|
#ifdef DBG_PERF
|
||||||
|
uint32_t dbg_maxlen;
|
||||||
|
#endif /* DBG_PERF */
|
||||||
|
#ifdef DQLOCK_MUTEX
|
||||||
|
SCMutex m;
|
||||||
|
#elif defined DQLOCK_SPIN
|
||||||
|
SCSpinlock s;
|
||||||
|
#else
|
||||||
|
#error Enable DQLOCK_SPIN or DQLOCK_MUTEX
|
||||||
|
#endif
|
||||||
|
} DefragTrackerQueue;
|
||||||
|
|
||||||
|
#ifdef DQLOCK_SPIN
|
||||||
|
#define DQLOCK_INIT(q) SCSpinInit(&(q)->s, 0)
|
||||||
|
#define DQLOCK_DESTROY(q) SCSpinDestroy(&(q)->s)
|
||||||
|
#define DQLOCK_LOCK(q) SCSpinLock(&(q)->s)
|
||||||
|
#define DQLOCK_TRYLOCK(q) SCSpinTrylock(&(q)->s)
|
||||||
|
#define DQLOCK_UNLOCK(q) SCSpinUnlock(&(q)->s)
|
||||||
|
#elif defined DQLOCK_MUTEX
|
||||||
|
#define DQLOCK_INIT(q) SCMutexInit(&(q)->m, NULL)
|
||||||
|
#define DQLOCK_DESTROY(q) SCMutexDestroy(&(q)->m)
|
||||||
|
#define DQLOCK_LOCK(q) SCMutexLock(&(q)->m)
|
||||||
|
#define DQLOCK_TRYLOCK(q) SCMutexTrylock(&(q)->m)
|
||||||
|
#define DQLOCK_UNLOCK(q) SCMutexUnlock(&(q)->m)
|
||||||
|
#else
|
||||||
|
#error Enable DQLOCK_SPIN or DQLOCK_MUTEX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* prototypes */
|
||||||
|
DefragTrackerQueue *DefragTrackerQueueNew();
|
||||||
|
DefragTrackerQueue *DefragTrackerQueueInit(DefragTrackerQueue *);
|
||||||
|
void DefragTrackerQueueDestroy (DefragTrackerQueue *);
|
||||||
|
|
||||||
|
void DefragTrackerEnqueue (DefragTrackerQueue *, DefragTracker *);
|
||||||
|
DefragTracker *DefragTrackerDequeue (DefragTrackerQueue *);
|
||||||
|
uint32_t DefragTrackerQueueLen(DefragTrackerQueue *);
|
||||||
|
|
||||||
|
#endif /* __DEFRAG_QUEUE_H__ */
|
||||||
|
|
@ -0,0 +1,150 @@
|
|||||||
|
/* 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>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "suricata-common.h"
|
||||||
|
#include "defrag.h"
|
||||||
|
#include "defrag-hash.h"
|
||||||
|
|
||||||
|
uint32_t DefragTrackerGetSpareCount(void) {
|
||||||
|
return DefragTrackerSpareQueueGetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t DefragTrackerGetActiveCount(void) {
|
||||||
|
return SC_ATOMIC_GET(defragtracker_counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \internal
|
||||||
|
* \brief See if we can really discard this tracker. Check use_cnt reference.
|
||||||
|
*
|
||||||
|
* \param dt tracker
|
||||||
|
* \param ts timestamp
|
||||||
|
*
|
||||||
|
* \retval 0 not timed out just yet
|
||||||
|
* \retval 1 fully timed out, lets kill it
|
||||||
|
*/
|
||||||
|
static int DefragTrackerTimedOut(DefragTracker *dt, struct timeval *ts) {
|
||||||
|
/** never prune a trackers that is used by a packet
|
||||||
|
* we are currently processing in one of the threads */
|
||||||
|
if (SC_ATOMIC_GET(dt->use_cnt) > 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* retain if remove is not set and not timed out */
|
||||||
|
if (!dt->remove && dt->timeout > ts->tv_sec)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
*
|
||||||
|
* \brief check all trackers in a hash row for timing out
|
||||||
|
*
|
||||||
|
* \param hb tracker hash row *LOCKED*
|
||||||
|
* \param dt last tracker in the hash row
|
||||||
|
* \param ts timestamp
|
||||||
|
*
|
||||||
|
* \retval cnt timed out tracker
|
||||||
|
*/
|
||||||
|
static uint32_t DefragTrackerHashRowTimeout(DefragTrackerHashRow *hb, DefragTracker *dt, struct timeval *ts)
|
||||||
|
{
|
||||||
|
uint32_t cnt = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (SCMutexTrylock(&dt->lock) != 0) {
|
||||||
|
dt = dt->hprev;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefragTracker *next_dt = dt->hprev;
|
||||||
|
|
||||||
|
/* check if the tracker is fully timed out and
|
||||||
|
* ready to be discarded. */
|
||||||
|
if (DefragTrackerTimedOut(dt, ts) == 1) {
|
||||||
|
/* remove from the hash */
|
||||||
|
if (dt->hprev != NULL)
|
||||||
|
dt->hprev->hnext = dt->hnext;
|
||||||
|
if (dt->hnext != NULL)
|
||||||
|
dt->hnext->hprev = dt->hprev;
|
||||||
|
if (hb->head == dt)
|
||||||
|
hb->head = dt->hnext;
|
||||||
|
if (hb->tail == dt)
|
||||||
|
hb->tail = dt->hprev;
|
||||||
|
|
||||||
|
dt->hnext = NULL;
|
||||||
|
dt->hprev = NULL;
|
||||||
|
|
||||||
|
DefragTrackerClearMemory(dt);
|
||||||
|
|
||||||
|
/* no one is referring to this tracker, use_cnt 0, removed from hash
|
||||||
|
* so we can unlock it and move it back to the spare queue. */
|
||||||
|
SCMutexUnlock(&dt->lock);
|
||||||
|
|
||||||
|
/* move to spare list */
|
||||||
|
DefragTrackerMoveToSpare(dt);
|
||||||
|
|
||||||
|
cnt++;
|
||||||
|
} else {
|
||||||
|
SCMutexUnlock(&dt->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
dt = next_dt;
|
||||||
|
} while (dt != NULL);
|
||||||
|
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief time out tracker from the hash
|
||||||
|
*
|
||||||
|
* \param ts timestamp
|
||||||
|
*
|
||||||
|
* \retval cnt number of timed out tracker
|
||||||
|
*/
|
||||||
|
uint32_t DefragTimeoutHash(struct timeval *ts) {
|
||||||
|
uint32_t idx = 0;
|
||||||
|
uint32_t cnt = 0;
|
||||||
|
|
||||||
|
for (idx = 0; idx < defrag_config.hash_size; idx++) {
|
||||||
|
DefragTrackerHashRow *hb = &defragtracker_hash[idx];
|
||||||
|
if (hb == NULL)
|
||||||
|
continue;
|
||||||
|
if (DRLOCK_TRYLOCK(hb) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* defrag hash bucket is now locked */
|
||||||
|
|
||||||
|
if (hb->tail == NULL) {
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we have a tracker, or more than one */
|
||||||
|
cnt += DefragTrackerHashRowTimeout(hb, hb->tail, ts);
|
||||||
|
DRLOCK_UNLOCK(hb);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
|||||||
|
/* 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>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DEFRAG_TIMEOUT_H__
|
||||||
|
#define __DEFRAG_TIMEOUT_H__
|
||||||
|
|
||||||
|
uint32_t DefragTimeoutHash(struct timeval *ts);
|
||||||
|
|
||||||
|
uint32_t DefragGetSpareCount(void);
|
||||||
|
uint32_t DefragGetActiveCount(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue