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
Victor Julien 13 years ago
parent c91c359692
commit 7a044a99ee

@ -301,6 +301,9 @@ app-layer-ssh.c app-layer-ssh.h \
app-layer-tls-handshake.c app-layer-tls-handshake.h \ app-layer-tls-handshake.c app-layer-tls-handshake.h \
app-layer-smtp.c app-layer-smtp.h \ app-layer-smtp.c app-layer-smtp.h \
defrag.c defrag.h \ defrag.c defrag.h \
defrag-hash.c defrag-hash.h \
defrag-queue.c defrag-queue.h \
defrag-timeout.c defrag-timeout.h \
output.c output.h \ output.c output.h \
win32-misc.c win32-misc.h \ win32-misc.c win32-misc.h \
win32-service.c win32-service.h \ win32-service.c win32-service.h \

@ -527,7 +527,7 @@ void DecodeIPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt,
/* If a fragment, pass off for re-assembly. */ /* If a fragment, pass off for re-assembly. */
if (unlikely(IPV4_GET_IPOFFSET(p) > 0 || IPV4_GET_MF(p) == 1)) { if (unlikely(IPV4_GET_IPOFFSET(p) > 0 || IPV4_GET_MF(p) == 1)) {
Packet *rp = Defrag(tv, dtv, NULL, p); Packet *rp = Defrag(tv, dtv, p);
if (rp != NULL) { if (rp != NULL) {
/* Got re-assembled packet, re-run through decoder. */ /* Got re-assembled packet, re-run through decoder. */
DecodeIPV4(tv, dtv, rp, (void *)rp->ip4h, IPV4_GET_IPLEN(rp), pq); DecodeIPV4(tv, dtv, rp, (void *)rp->ip4h, IPV4_GET_IPLEN(rp), pq);
@ -1558,6 +1558,7 @@ int DecodeIPV4DefragTest01(void)
PACKET_INITIALIZE(p); PACKET_INITIALIZE(p);
FlowInitConfig(FLOW_QUIET); FlowInitConfig(FLOW_QUIET);
DefragInit();
PacketCopyData(p, pkt1, sizeof(pkt1)); PacketCopyData(p, pkt1, sizeof(pkt1));
DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN, DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN,
@ -1622,6 +1623,7 @@ int DecodeIPV4DefragTest01(void)
SCFree(tp); SCFree(tp);
end: end:
DefragDestroy();
FlowShutdown(); FlowShutdown();
PACKET_CLEANUP(p); PACKET_CLEANUP(p);
SCFree(p); SCFree(p);
@ -1686,7 +1688,7 @@ int DecodeIPV4DefragTest02(void)
ThreadVars tv; ThreadVars tv;
DecodeThreadVars dtv; DecodeThreadVars dtv;
PacketQueue pq; PacketQueue pq;
int result = 1; int result = 0;
memset(&tv, 0, sizeof(ThreadVars)); memset(&tv, 0, sizeof(ThreadVars));
memset(&dtv, 0, sizeof(DecodeThreadVars)); memset(&dtv, 0, sizeof(DecodeThreadVars));
@ -1694,13 +1696,13 @@ int DecodeIPV4DefragTest02(void)
PACKET_INITIALIZE(p); PACKET_INITIALIZE(p);
FlowInitConfig(FLOW_QUIET); FlowInitConfig(FLOW_QUIET);
DefragInit();
PacketCopyData(p, pkt1, sizeof(pkt1)); PacketCopyData(p, pkt1, sizeof(pkt1));
DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN, DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN,
GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq); GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq);
if (p->tcph != NULL) { if (p->tcph != NULL) {
printf("tcp header should be NULL for ip fragment, but it isn't\n"); printf("tcp header should be NULL for ip fragment, but it isn't\n");
result = 0;
goto end; goto end;
} }
PACKET_DO_RECYCLE(p); PACKET_DO_RECYCLE(p);
@ -1710,7 +1712,6 @@ int DecodeIPV4DefragTest02(void)
GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq); GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq);
if (p->tcph != NULL) { if (p->tcph != NULL) {
printf("tcp header should be NULL for ip fragment, but it isn't\n"); printf("tcp header should be NULL for ip fragment, but it isn't\n");
result = 0;
goto end; goto end;
} }
PACKET_DO_RECYCLE(p); PACKET_DO_RECYCLE(p);
@ -1721,45 +1722,41 @@ int DecodeIPV4DefragTest02(void)
GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq); GET_PKT_LEN(p) - ETHERNET_HEADER_LEN, &pq);
if (p->tcph != NULL) { if (p->tcph != NULL) {
printf("tcp header should be NULL for ip fragment, but it isn't\n"); printf("tcp header should be NULL for ip fragment, but it isn't\n");
result = 0;
goto end; goto end;
} }
Packet *tp = PacketDequeue(&pq); Packet *tp = PacketDequeue(&pq);
if (tp == NULL) { if (tp == NULL) {
printf("Failed to get defragged pseudo packet\n"); printf("Failed to get defragged pseudo packet\n");
result = 0;
goto end; goto end;
} }
if (tp->recursion_level != p->recursion_level) { if (tp->recursion_level != p->recursion_level) {
printf("defragged pseudo packet's and parent packet's recursion " printf("defragged pseudo packet's and parent packet's recursion "
"level don't match\n %d != %d", "level don't match %d != %d: ",
tp->recursion_level, p->recursion_level); tp->recursion_level, p->recursion_level);
result = 0;
goto end; goto end;
} }
if (tp->ip4h == NULL || tp->tcph == NULL) { if (tp->ip4h == NULL || tp->tcph == NULL) {
printf("pseudo packet's ip header and tcp header shouldn't be NULL, " printf("pseudo packet's ip header and tcp header shouldn't be NULL, "
"but it is\n"); "but it is\n");
result = 0;
goto end; goto end;
} }
if (GET_PKT_LEN(tp) != sizeof(tunnel_pkt)) { if (GET_PKT_LEN(tp) != sizeof(tunnel_pkt)) {
printf("defragged pseudo packet's and parent packet's pkt lens " printf("defragged pseudo packet's and parent packet's pkt lens "
"don't match\n %u != %"PRIuMAX, "don't match %u != %"PRIuMAX": ",
GET_PKT_LEN(tp), (uintmax_t)sizeof(tunnel_pkt)); GET_PKT_LEN(tp), (uintmax_t)sizeof(tunnel_pkt));
result = 0;
goto end; goto end;
} }
if (memcmp(GET_PKT_DATA(tp), tunnel_pkt, sizeof(tunnel_pkt)) != 0) { if (memcmp(GET_PKT_DATA(tp), tunnel_pkt, sizeof(tunnel_pkt)) != 0) {
result = 0; goto end;
goto end;
} }
result = 1;
PACKET_CLEANUP(p); PACKET_CLEANUP(p);
SCFree(tp); SCFree(tp);
end: end:
DefragDestroy();
FlowShutdown(); FlowShutdown();
PACKET_CLEANUP(p); PACKET_CLEANUP(p);
SCFree(p); SCFree(p);
@ -1828,6 +1825,7 @@ int DecodeIPV4DefragTest03(void)
PACKET_INITIALIZE(p); PACKET_INITIALIZE(p);
FlowInitConfig(FLOW_QUIET); FlowInitConfig(FLOW_QUIET);
DefragInit();
PacketCopyData(p, pkt, sizeof(pkt)); PacketCopyData(p, pkt, sizeof(pkt));
DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN, DecodeIPV4(&tv, &dtv, p, GET_PKT_DATA(p) + ETHERNET_HEADER_LEN,
@ -1918,6 +1916,7 @@ int DecodeIPV4DefragTest03(void)
SCFree(tp); SCFree(tp);
end: end:
DefragDestroy();
FlowShutdown(); FlowShutdown();
PACKET_CLEANUP(p); PACKET_CLEANUP(p);
SCFree(p); SCFree(p);

@ -564,7 +564,7 @@ void DecodeIPV6(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt,
/* Pass to defragger if a fragment. */ /* Pass to defragger if a fragment. */
if (IPV6_EXTHDR_ISSET_FH(p)) { if (IPV6_EXTHDR_ISSET_FH(p)) {
Packet *rp = Defrag(tv, dtv, NULL, p); Packet *rp = Defrag(tv, dtv, p);
if (rp != NULL) { if (rp != NULL) {
DecodeIPV6(tv, dtv, rp, (uint8_t *)rp->ip6h, IPV6_GET_PLEN(rp) + IPV6_HEADER_LEN, pq); DecodeIPV6(tv, dtv, rp, (uint8_t *)rp->ip6h, IPV6_GET_PLEN(rp) + IPV6_HEADER_LEN, pq);
PacketEnqueue(pq, rp); PacketEnqueue(pq, rp);
@ -725,6 +725,7 @@ static int DecodeIPV6FragTest01 (void) {
PacketQueue pq; PacketQueue pq;
FlowInitConfig(FLOW_QUIET); FlowInitConfig(FLOW_QUIET);
DefragInit();
memset(&pq, 0, sizeof(PacketQueue)); memset(&pq, 0, sizeof(PacketQueue));
memset(&tv, 0, sizeof(ThreadVars)); memset(&tv, 0, sizeof(ThreadVars));
@ -765,6 +766,7 @@ end:
PACKET_CLEANUP(p2); PACKET_CLEANUP(p2);
SCFree(p1); SCFree(p1);
SCFree(p2); SCFree(p2);
DefragDestroy();
FlowShutdown(); FlowShutdown();
return result; return result;
} }

@ -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

@ -24,12 +24,113 @@
#ifndef __DEFRAG_H__ #ifndef __DEFRAG_H__
#define __DEFRAG_H__ #define __DEFRAG_H__
typedef struct _DefragContext DefragContext; #include "util-pool.h"
/**
* A context for an instance of a fragmentation re-assembler, in case
* we ever need more than one.
*/
typedef struct DefragContext_ {
Pool *frag_pool; /**< Pool of fragments. */
SCMutex frag_pool_lock;
time_t timeout; /**< Default timeout. */
} DefragContext;
/**
* Storage for an individual fragment.
*/
typedef struct Frag_ {
uint16_t offset; /**< The offset of this fragment, already
* multiplied by 8. */
uint16_t len; /**< The length of this fragment. */
uint8_t hlen; /**< The length of this fragments IP header. */
uint8_t more_frags:4; /**< More frags? */
uint8_t skip:4; /**< Skip this fragment during re-assembly. */
uint16_t ip_hdr_offset; /**< Offset in the packet where the IP
* header starts. */
uint16_t frag_hdr_offset; /**< Offset in the packet where the frag
* header starts. */
uint16_t data_offset; /**< Offset to the packet data. */
uint16_t data_len; /**< Length of data. */
uint16_t ltrim; /**< Number of leading bytes to trim when
* re-assembling the packet. */
uint8_t *pkt; /**< The actual packet. */
#ifdef DEBUG
uint64_t pcap_cnt; /**< pcap_cnt of original packet */
#endif
TAILQ_ENTRY(Frag_) next; /**< Pointer to next fragment for tailq. */
} Frag;
/** \brief Reset tracker fields except "lock" */
#define DEFRAG_TRACKER_RESET(t) { \
(t)->timeout = 0; \
(t)->id = 0; \
(t)->policy = 0; \
(t)->af = 0; \
(t)->seen_last = 0; \
(t)->remove = 0; \
CLEAR_ADDR(&(t)->src_addr); \
CLEAR_ADDR(&(t)->dst_addr); \
(t)->frags.tqh_first = NULL; \
(t)->frags.tqh_last = NULL; \
}
/**
* A defragmentation tracker. Used to track fragments that make up a
* single packet.
*/
typedef struct DefragTracker_ {
SCMutex lock; /**< Mutex for locking list operations on
* this tracker. */
uint32_t id; /**< IP ID for this tracker. 32 bits for IPv6, 16
* for IPv4. */
uint8_t policy; /**< Reassembly policy this tracker will use. */
uint8_t af; /**< Address family for this tracker, AF_INET or
* AF_INET6. */
uint8_t seen_last; /**< Has this tracker seen the last fragment? */
uint8_t remove; /**< remove */
Address src_addr; /**< Source address for this tracker. */
Address dst_addr; /**< Destination address for this tracker. */
uint32_t timeout; /**< When this tracker will timeout. */
/** use cnt, reference counter */
SC_ATOMIC_DECLARE(unsigned short, use_cnt);
TAILQ_HEAD(frag_tailq, Frag_) frags; /**< Head of list of fragments. */
/** hash pointers, protected by hash row mutex/spin */
struct DefragTracker_ *hnext;
struct DefragTracker_ *hprev;
/** list pointers, protected by tracker-queue mutex/spin */
struct DefragTracker_ *lnext;
struct DefragTracker_ *lprev;
} DefragTracker;
void DefragInit(void); void DefragInit(void);
void DefragDestroy(void); void DefragDestroy(void);
void DefragReload(void); /**< use only in unittests */
Packet *Defrag(ThreadVars *, DecodeThreadVars *, DefragContext *, Packet *); uint8_t DefragGetOsPolicy(Packet *);
void DefragTrackerFreeFrags(DefragTracker *);
Packet *Defrag(ThreadVars *, DecodeThreadVars *, Packet *);
void DefragRegisterTests(void); void DefragRegisterTests(void);
#endif /* __DEFRAG_H__ */ #endif /* __DEFRAG_H__ */

@ -476,6 +476,7 @@ void *FlowManagerThread(void *td)
FlowTimeoutHash(&ts, 0 /* check all */, &counters); FlowTimeoutHash(&ts, 0 /* check all */, &counters);
DefragTimeoutHash(&ts);
//uint32_t hosts_pruned = //uint32_t hosts_pruned =
HostTimeoutHash(&ts); HostTimeoutHash(&ts);
/* /*

@ -233,6 +233,7 @@ const char * SCErrorToString(SCError err)
CASE_CODE (SC_ERR_EVENT_ENGINE); CASE_CODE (SC_ERR_EVENT_ENGINE);
CASE_CODE (SC_ERR_NO_LUAJIT_SUPPORT); CASE_CODE (SC_ERR_NO_LUAJIT_SUPPORT);
CASE_CODE (SC_ERR_LUAJIT_ERROR); CASE_CODE (SC_ERR_LUAJIT_ERROR);
CASE_CODE (SC_ERR_DEFRAG_INIT);
default: default:
return "UNKNOWN_ERROR"; return "UNKNOWN_ERROR";
} }

@ -248,6 +248,7 @@ typedef enum {
SC_ERR_EVENT_ENGINE, SC_ERR_EVENT_ENGINE,
SC_ERR_NO_LUAJIT_SUPPORT, SC_ERR_NO_LUAJIT_SUPPORT,
SC_ERR_LUAJIT_ERROR, SC_ERR_LUAJIT_ERROR,
SC_ERR_DEFRAG_INIT,
} SCError; } SCError;
const char *SCErrorToString(SCError); const char *SCErrorToString(SCError);

@ -458,6 +458,8 @@ pattern-matcher:
# Defrag settings: # Defrag settings:
defrag: defrag:
memcap: 32mb
hash-size: 65536
trackers: 65535 # number of defragmented flows to follow trackers: 65535 # number of defragmented flows to follow
max-frags: 65535 # number of fragments to keep (higher than trackers) max-frags: 65535 # number of fragments to keep (higher than trackers)
prealloc: yes prealloc: yes

Loading…
Cancel
Save