mirror of https://github.com/OISF/suricata
Stream reassembly update and WIP code for L7 modules.
parent
2c8df73d24
commit
1c2240cfeb
@ -0,0 +1,132 @@
|
||||
#include "eidps.h"
|
||||
#include "debug.h"
|
||||
#include "decode.h"
|
||||
#include "threads.h"
|
||||
|
||||
#include "util-print.h"
|
||||
#include "util-pool.h"
|
||||
|
||||
#include "stream-tcp-private.h"
|
||||
#include "stream.h"
|
||||
|
||||
#define INSPECT_BYTES 64
|
||||
|
||||
#define PROTO_UNKNOWN 0
|
||||
#define PROTO_HTTP 1
|
||||
#define PROTO_FTP 2
|
||||
#define PROTO_SMTP 3
|
||||
|
||||
#define TYPE_PROTO 0
|
||||
#define TYPE_BUF 1
|
||||
|
||||
/* XXX type can be 1 bit, 7 bit for proto */
|
||||
typedef struct _L7AppDetectDataProto {
|
||||
u_int8_t type;
|
||||
u_int8_t proto;
|
||||
} L7AppDetectDataProto;
|
||||
|
||||
static Pool *l7appdetect_proto_pool = NULL;
|
||||
|
||||
void *L7AppDetectProtoAlloc(void *null) {
|
||||
L7AppDetectDataProto *d = malloc(sizeof(L7AppDetectDataProto));
|
||||
if (d == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
d->type = TYPE_PROTO;
|
||||
d->proto = PROTO_UNKNOWN;
|
||||
return d;
|
||||
}
|
||||
#define L7AppDetectProtoFree free
|
||||
|
||||
void L7AppDetectThreadInit(void) {
|
||||
/* allocate 2 pools, 1 for proto objects, 1 for bufs. Normal stream will
|
||||
* jump straigth to protos so we alloc a lot less bufs */
|
||||
l7appdetect_proto_pool = PoolInit(262144, 32768, L7AppDetectProtoAlloc, NULL, L7AppDetectProtoFree);
|
||||
if (l7appdetect_proto_pool == NULL) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
u_int8_t L7AppDetectGetProto(u_int8_t *buf, u_int16_t buflen) {
|
||||
if (buflen < INSPECT_BYTES)
|
||||
return PROTO_UNKNOWN;
|
||||
|
||||
/* XXX do actual detect */
|
||||
printf("L7AppDetectGetProto: protocol detection goes here.\n");
|
||||
return PROTO_HTTP;
|
||||
}
|
||||
|
||||
void *L7AppDetectThread(void *td)
|
||||
{
|
||||
ThreadVars *tv = (ThreadVars *)td;
|
||||
char run = TRUE;
|
||||
u_int8_t l7_data_id = 0;
|
||||
|
||||
/* get the stream msg queue for this thread */
|
||||
StreamMsgQueue *stream_q = StreamMsgQueueGetByPort(0);
|
||||
StreamMsgQueueSetMinInitChunkLen(stream_q, INSPECT_BYTES);
|
||||
|
||||
while(run) {
|
||||
/* grab a msg, can return NULL on signals */
|
||||
StreamMsg *smsg = StreamMsgGetFromQueue(stream_q);
|
||||
//printf("L7AppDetectThread: smsg %p\n", smsg);
|
||||
|
||||
if (smsg != NULL) {
|
||||
/* keep the flow locked during operation.
|
||||
* XXX we may be better off adding a mutex
|
||||
* to the l7data object */
|
||||
mutex_lock(&smsg->flow->m);
|
||||
|
||||
TcpSession *ssn = smsg->flow->stream;
|
||||
if (ssn != NULL) {
|
||||
if (ssn->l7data == NULL) {
|
||||
StreamL7DataPtrInit(ssn,1); /* XXX we can use a pool here,
|
||||
or make it part of the stream setup */
|
||||
}
|
||||
void *l7_data_ptr = ssn->l7data[l7_data_id];
|
||||
|
||||
if (smsg->flags & STREAM_START) {
|
||||
//printf("L7AppDetectThread: stream initializer (len %u (%u))\n", smsg->init.data_len, MSG_INIT_DATA_SIZE);
|
||||
|
||||
//printf("=> Init Stream Data -- start\n");
|
||||
//PrintRawDataFp(stdout, smsg->init.data, smsg->init.data_len);
|
||||
//printf("=> Init Stream Data -- end\n");
|
||||
|
||||
if (l7_data_ptr == NULL) {
|
||||
L7AppDetectDataProto *l7proto = (L7AppDetectDataProto *)PoolGet(l7appdetect_proto_pool);
|
||||
if (l7proto != NULL) {
|
||||
l7proto->type = TYPE_PROTO;
|
||||
l7proto->proto = L7AppDetectGetProto(smsg->data.data, smsg->data.data_len);
|
||||
|
||||
ssn->l7data[l7_data_id] = (void *)l7proto;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//printf("L7AppDetectThread: stream data (len %u (%u))\n", smsg->data.data_len, MSG_DATA_SIZE);
|
||||
|
||||
/* if we don't have a data object here we are not getting it
|
||||
* a start msg should have gotten us one */
|
||||
if (l7_data_ptr != NULL) {
|
||||
L7AppDetectDataProto *l7proto = (L7AppDetectDataProto *)l7_data_ptr;
|
||||
printf("L7AppDetectThread: already established that the proto is %u\n", l7proto->proto);
|
||||
} else {
|
||||
printf("L7AppDetectThread: smsg not start, but no l7 data? Weird\n");
|
||||
}
|
||||
//printf("=> Stream Data -- start\n");
|
||||
//PrintRawDataFp(stdout, smsg->data.data, smsg->data.data_len);
|
||||
//printf("=> Stream Data -- end\n");
|
||||
}
|
||||
|
||||
mutex_unlock(&smsg->flow->m);
|
||||
}
|
||||
StreamMsgReturnToPool(smsg);
|
||||
}
|
||||
|
||||
if (tv->flags & THV_KILL)
|
||||
run = 0;
|
||||
}
|
||||
|
||||
pthread_exit((void *) 0);
|
||||
}
|
||||
|
@ -0,0 +1,6 @@
|
||||
#ifndef __L7_APP_DETECT_H__
|
||||
#define __L7_APP_DETECT_H__
|
||||
|
||||
void *L7AppDetectThread(void *td);
|
||||
|
||||
#endif /* __L7_APP_DETECT_H__ */
|
@ -0,0 +1,167 @@
|
||||
/* Copyright (c) 2009 Victor Julien <victor@inliniac.net> */
|
||||
|
||||
#include "eidps.h"
|
||||
#include "decode.h"
|
||||
#include "threads.h"
|
||||
|
||||
#include "stream.h"
|
||||
|
||||
#include "util-pool.h"
|
||||
|
||||
static StreamMsgQueue stream_q;
|
||||
static u_int16_t toserver_min_init_chunk_len = 0;
|
||||
static u_int16_t toserver_min_chunk_len = 0;
|
||||
static u_int16_t toclient_min_init_chunk_len = 0;
|
||||
static u_int16_t toclient_min_chunk_len = 0;
|
||||
|
||||
static Pool *stream_msg_pool = NULL;
|
||||
static pthread_mutex_t stream_msg_pool_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
void *StreamMsgAlloc(void *null) {
|
||||
StreamMsg *s = malloc(sizeof(StreamMsg));
|
||||
if (s == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(s, 0, sizeof(StreamMsg));
|
||||
return s;
|
||||
}
|
||||
|
||||
void StreamMsgFree(void *ptr) {
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
|
||||
StreamMsg *s = (StreamMsg *)ptr;
|
||||
free(s);
|
||||
return;
|
||||
}
|
||||
|
||||
static void StreamMsgEnqueue (StreamMsgQueue *q, StreamMsg *s) {
|
||||
/* more packets in queue */
|
||||
if (q->top != NULL) {
|
||||
s->next = q->top;
|
||||
q->top->prev = s;
|
||||
q->top = s;
|
||||
/* only packet */
|
||||
} else {
|
||||
q->top = s;
|
||||
q->bot = s;
|
||||
}
|
||||
q->len++;
|
||||
#ifdef DBG_PERF
|
||||
if (q->len > q->dbg_maxlen)
|
||||
q->dbg_maxlen = q->len;
|
||||
#endif /* DBG_PERF */
|
||||
}
|
||||
|
||||
static StreamMsg *StreamMsgDequeue (StreamMsgQueue *q) {
|
||||
/* if the queue is empty there are no packets left.
|
||||
* In that case we sleep and try again. */
|
||||
if (q->len == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* pull the bottom packet from the queue */
|
||||
StreamMsg *s = q->bot;
|
||||
|
||||
/* more packets in queue */
|
||||
if (q->bot->prev != NULL) {
|
||||
q->bot = q->bot->prev;
|
||||
q->bot->next = NULL;
|
||||
/* just the one we remove, so now empty */
|
||||
} else {
|
||||
q->top = NULL;
|
||||
q->bot = NULL;
|
||||
}
|
||||
q->len--;
|
||||
|
||||
s->next = NULL;
|
||||
s->prev = NULL;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Used by stream reassembler to get msgs */
|
||||
StreamMsg *StreamMsgGetFromPool(void)
|
||||
{
|
||||
mutex_lock(&stream_msg_pool_mutex);
|
||||
StreamMsg *s = (StreamMsg *)PoolGet(stream_msg_pool);
|
||||
mutex_unlock(&stream_msg_pool_mutex);
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Used by l7inspection to return msgs to pool */
|
||||
void StreamMsgReturnToPool(StreamMsg *s) {
|
||||
mutex_lock(&stream_msg_pool_mutex);
|
||||
PoolReturn(stream_msg_pool, (void *)s);
|
||||
mutex_unlock(&stream_msg_pool_mutex);
|
||||
}
|
||||
|
||||
/* Used by l7inspection to get msgs with data */
|
||||
StreamMsg *StreamMsgGetFromQueue(StreamMsgQueue *q)
|
||||
{
|
||||
mutex_lock(&q->mutex_q);
|
||||
if (q->len == 0) {
|
||||
/* if we have no stream msgs in queue, wait... */
|
||||
pthread_cond_wait(&q->cond_q, &q->mutex_q);
|
||||
}
|
||||
if (q->len > 0) {
|
||||
StreamMsg *s = StreamMsgDequeue(q);
|
||||
mutex_unlock(&q->mutex_q);
|
||||
return s;
|
||||
} else {
|
||||
/* return NULL if we have no stream msg. Should only happen on signals. */
|
||||
mutex_unlock(&q->mutex_q);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Used by stream reassembler to fill the queue for l7inspect reading */
|
||||
void StreamMsgPutInQueue(StreamMsg *s)
|
||||
{
|
||||
StreamMsgQueue *q = &stream_q;
|
||||
|
||||
mutex_lock(&q->mutex_q);
|
||||
StreamMsgEnqueue(q, s);
|
||||
printf("StreamMsgPutInQueue: q->len %u\n", q->len);
|
||||
pthread_cond_signal(&q->cond_q);
|
||||
mutex_unlock(&q->mutex_q);
|
||||
}
|
||||
|
||||
void StreamMsgQueuesInit(void) {
|
||||
memset(&stream_q, 0, sizeof(stream_q));
|
||||
|
||||
stream_msg_pool = PoolInit(5000,250,StreamMsgAlloc,NULL,StreamMsgFree);
|
||||
if (stream_msg_pool == NULL)
|
||||
exit(1); /* XXX */
|
||||
}
|
||||
|
||||
StreamMsgQueue *StreamMsgQueueGetByPort(u_int16_t port) {
|
||||
/* XXX implement this */
|
||||
return &stream_q;
|
||||
}
|
||||
|
||||
/* XXX hack */
|
||||
void StreamMsgSignalQueueHack(void) {
|
||||
pthread_cond_signal(&stream_q.cond_q);
|
||||
}
|
||||
|
||||
void StreamMsgQueueSetMinInitChunkLen(u_int8_t dir, u_int16_t len) {
|
||||
|
||||
}
|
||||
|
||||
u_int16_t StreamMsgQueueGetMinInitChunkLen(u_int8_t dir) {
|
||||
if (dir == FLOW_PKT_TOSERVER) {
|
||||
return toserver_min_init_chunk_len;
|
||||
} else {
|
||||
return toclient_min_init_chunk_len;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
u_int16_t StreamMsgQueueGetMinChunkLen(u_int8_t dir) {
|
||||
if (dir == FLOW_PKT_TOSERVER) {
|
||||
return toserver_min_chunk_len;
|
||||
} else {
|
||||
return toclient_min_chunk_len;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
/* API for stream handling */
|
||||
|
||||
#ifndef __STREAM_H__
|
||||
#define __STREAM_H__
|
||||
|
||||
#include "flow.h"
|
||||
|
||||
#define STREAM_START 0x01
|
||||
#define STREAM_EOF 0x02
|
||||
#define STREAM_TOSERVER 0x04
|
||||
#define STREAM_TOCLIENT 0x08
|
||||
#define STREAM_GAP 0x10
|
||||
|
||||
#define MSG_DATA_SIZE 512
|
||||
|
||||
typedef struct _StreamMsg {
|
||||
u_int32_t id; /* unique stream id */
|
||||
u_int8_t flags; /* msg flags */
|
||||
Flow *flow; /* parent flow */
|
||||
|
||||
union {
|
||||
/* case STREAM_START */
|
||||
struct {
|
||||
Address src_ip, dst_ip;
|
||||
Port src_port, dst_port;
|
||||
u_int8_t data[MSG_DATA_SIZE];
|
||||
u_int16_t data_len;
|
||||
} data;
|
||||
/* case STREAM_GAP */
|
||||
struct {
|
||||
u_int32_t gap_size;
|
||||
} gap;
|
||||
};
|
||||
|
||||
struct _StreamMsg *next;
|
||||
struct _StreamMsg *prev;
|
||||
} StreamMsg;
|
||||
|
||||
typedef struct _StreamMsgQueue {
|
||||
StreamMsg *top;
|
||||
StreamMsg *bot;
|
||||
u_int16_t len;
|
||||
pthread_mutex_t mutex_q;
|
||||
pthread_cond_t cond_q;
|
||||
#ifdef DBG_PERF
|
||||
u_int16_t dbg_maxlen;
|
||||
#endif /* DBG_PERF */
|
||||
} StreamMsgQueue;
|
||||
|
||||
/* prototypes */
|
||||
void StreamMsgQueuesInit(void);
|
||||
|
||||
StreamMsg *StreamMsgGetFromPool(void);
|
||||
void StreamMsgReturnToPool(StreamMsg *);
|
||||
StreamMsg *StreamMsgGetFromQueue(StreamMsgQueue *);
|
||||
void StreamMsgPutInQueue(StreamMsg *);
|
||||
|
||||
StreamMsgQueue *StreamMsgQueueGetByPort(u_int16_t);
|
||||
|
||||
#endif /* __STREAM_H__ */
|
||||
|
Loading…
Reference in New Issue