First cut at IPv6 re-assembly. Only BSD policy for now, packets don't actually get passed to it yet though.

remotes/origin/master-1.0.x
Jason Ish 16 years ago committed by Victor Julien
parent c816af822e
commit 0d92f0018b

@ -5,6 +5,7 @@
#include "decode-ipv6.h"
#include "decode-icmpv6.h"
#include "decode-events.h"
#include "defrag.h"
#include "util-debug.h"
#define IPV6_EXTHDRS ip6eh.ip6_exthdrs
@ -406,6 +407,16 @@ void DecodeIPV6(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt,
break;
}
#if 0
/* Pass to defragger if a fragment. */
if (IPV6_EXTHDR_ISSET_FH(p)) {
Packet *rp = Defrag6(tv, NULL, p);
if (rp != NULL) {
/* Reinject. */
}
}
#endif
#ifdef DEBUG
if (IPV6_EXTHDR_ISSET_FH(p)) {
printf("IPV6 FRAG - HDRLEN: %" PRIuMAX " NH: %" PRIu32 " OFFSET: %" PRIu32 " ID: %" PRIu32 "\n",

@ -23,6 +23,7 @@
#include "eidps.h"
#include "threads.h"
#include "conf.h"
#include "decode-ipv6.h"
#include "util-hashlist.h"
#include "util-pool.h"
#include "util-print.h"
@ -105,8 +106,21 @@ typedef struct _frag {
uint8_t more_frags; /**< More frags? */
uint16_t ipv6_hdr_offset; /**< Offset in the packet where the IPv6
* 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. */
uint8_t *pkt; /**< The actual packet. */
uint16_t ltrim; /**< Number of leading bytes to trim when
* re-assembling the packet. */
int8_t skip; /**< Skip this fragment during re-assembly. */
TAILQ_ENTRY(_frag) next; /**< Pointer to next fragment for tailq. */
} Frag;
@ -163,7 +177,8 @@ DumpFrags(DefragTracker *tracker)
printf("Dumping frags for packet: ID=%d\n", tracker->id);
TAILQ_FOREACH(frag, &tracker->frags, next) {
printf("-> Frag: Offset=%d, Len=%d\n", frag->offset, frag->len);
printf("-> Frag: frag_offset=%d, frag_len=%d, data_len=%d\n",
frag->offset, frag->len, frag->data_len);
PrintRawDataFp(stdout, frag->pkt, frag->len);
}
}
@ -660,6 +675,112 @@ done:
SCMutexUnlock(&tracker->lock);
}
static void
Defrag6InsertFrag(DefragContext *dc, DefragTracker *tracker, Packet *p)
{
int ltrim = 0;
/* We don't multiple by 8 here as this macro returns the value
* unshifted, which means its already the real offset. */
uint16_t frag_offset = IPV6_EXTHDR_GET_FH_OFFSET(p);
/* This is the offset of the start of the data in the packet that
* falls after the fragmentation header. */
uint16_t data_offset = (uint8_t *)p->ip6eh.ip6fh + sizeof(IPV6FragHdr) -
p->pkt;
/* The length of the (fragmented) data. This is the length of the
* data that falls after the fragmentation header. */
uint16_t data_len = IPV6_GET_PLEN(p) - (
((uint8_t *)p->ip6eh.ip6fh + sizeof(IPV6FragHdr)) -
((uint8_t *)p->ip6h + sizeof(IPV6Hdr)));
/* Where the fragment ends. */
uint16_t frag_end = frag_offset + data_len;
/* Offset in the packet to the IPv6 header. */
uint16_t ipv6_hdr_offset = (uint8_t *)p->ip6h - p->pkt;
/* Offset in the packet to the IPv6 frag header. */
uint16_t frag_hdr_offset = (uint8_t *)p->ip6eh.ip6fh - p->pkt;
/* Lock this tracker as we'll be doing list operations on it. */
SCMutexLock(&tracker->lock);
/* Update timeout. */
tracker->timeout = p->ts;
tracker->timeout.tv_sec += dc->timeout;
Frag *prev, *next;;
if (!TAILQ_EMPTY(&tracker->frags)) {
TAILQ_FOREACH(prev, &tracker->frags, next) {
ltrim = 0;
next = TAILQ_NEXT(prev, next);
switch (tracker->policy) {
case POLICY_BSD:
if (frag_offset < prev->offset + prev->data_len) {
if (frag_offset >= prev->offset) {
ltrim = prev->offset + prev->data_len - frag_offset;
}
if ((next != NULL) && (frag_end > next->offset)) {
next->ltrim = frag_end - next->offset;
}
else if ((frag_offset < prev->offset) &&
(frag_end >= prev->offset + prev->data_len)) {
prev->skip = 1;
}
goto insert;
}
break;
default:
break;
}
}
}
insert:
if (data_len - ltrim <= 0) {
goto done;
}
/* Allocate fragment and insert. */
SCMutexLock(&dc->frag_pool_lock);
Frag *new = PoolGet(dc->frag_pool);
SCMutexUnlock(&dc->frag_pool_lock);
if (new == NULL)
goto done;
new->pkt = malloc(p->pktlen);
if (new->pkt == NULL) {
SCMutexLock(&dc->frag_pool_lock);
PoolReturn(dc->frag_pool, new);
SCMutexUnlock(&dc->frag_pool_lock);
goto done;
}
memcpy(new->pkt, p->pkt + ltrim, p->pktlen - ltrim);
new->len = p->pktlen - ltrim;
new->offset = frag_offset + ltrim;
new->data_offset = data_offset;
new->data_len = data_len - ltrim;
new->ipv6_hdr_offset = ipv6_hdr_offset;
new->frag_hdr_offset = frag_hdr_offset;
Frag *frag;
TAILQ_FOREACH(frag, &tracker->frags, next) {
if (frag_offset < frag->offset)
break;
}
if (frag == NULL) {
TAILQ_INSERT_TAIL(&tracker->frags, new, next);
}
else {
TAILQ_INSERT_BEFORE(frag, new, next);
}
done:
SCMutexUnlock(&tracker->lock);
}
/**
* Attempt to re-assemble a packet.
*
@ -795,6 +916,112 @@ done:
return rp;
}
static Packet *
Defrag6Reassemble(ThreadVars *tv, DefragContext *dc, DefragTracker *tracker,
Packet *p)
{
Packet *rp = NULL;
/* Should not be here unless we have seen the last fragment. */
if (!tracker->seen_last)
return NULL;
/* Lock the tracker. */
SCMutexLock(&tracker->lock);
/* Check that we have all the data. Relies on the fact that
* fragments are inserted if frag_offset order. */
Frag *frag;
int len = 0;
TAILQ_FOREACH(frag, &tracker->frags, next) {
if (frag->skip)
continue;
if (frag == TAILQ_FIRST(&tracker->frags)) {
if (frag->offset != 0) {
printf("Defrag6Reassemble: no offset 0\n");
goto done;
}
len = frag->data_len;
}
else {
if (frag->offset > len) {
/* This fragment starts after the end of the previous
* fragment. We have a hole. */
printf("Defrag6Reassemble: hole: offset=%d, len=%d\n",
frag->offset, len);
goto done;
}
else {
len += frag->data_len;
}
}
}
/* Allocate a Packet for the reassembled packet. On failure we
* free all the resources held by this tracker. */
if (tv == NULL) {
/* Unit test. */
rp = SetupPkt();
}
else {
/* Not really a tunnel packet, but more of a pseudo packet.
* But for the most part we should get the same result. */
rp = TunnelPktSetup(tv, NULL, p, (uint8_t *)p->ip6h,
IPV6_GET_PLEN(p) + sizeof(IPV6Hdr), 0);
}
if (rp == NULL) {
SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate packet for fragmentation re-assembly, dumping fragments.");
SCMutexLock(&dc->frag_table_lock);
HashListTableRemove(dc->frag_table, tracker, sizeof(tracker));
SCMutexUnlock(&dc->frag_table_lock);
DefragTrackerReset(tracker);
SCMutexLock(&dc->tracker_pool_lock);
PoolReturn(dc->tracker_pool, tracker);
SCMutexUnlock(&dc->tracker_pool_lock);
goto done;
}
int payload_len = 0;
int fragmentable_offset = 0;
int pktlen = 0;
TAILQ_FOREACH(frag, &tracker->frags, next) {
if (frag->skip)
continue;
if (frag->offset == 0) {
/* This is the first packet, we use this packets link and
* IPv6 headers. We also copy in its data, but remove the
* fragmentation header. */
memcpy(rp->pkt, frag->pkt, frag->frag_hdr_offset);
memcpy(rp->pkt + frag->frag_hdr_offset,
frag->pkt + frag->frag_hdr_offset + sizeof(IPV6FragHdr),
frag->data_len);
rp->ip6h = (IPV6Hdr *)(rp->pkt + frag->ipv6_hdr_offset);
payload_len = ntohs(rp->ip6h->s_ip6_plen) - sizeof(IPV6FragHdr);
/* This is the start of the fragmentable portion of the
* first packet. All fragment offsets are relative to
* this. */
fragmentable_offset = frag->frag_hdr_offset;
pktlen = frag->ipv6_hdr_offset + sizeof(IPV6Hdr);
}
else {
memcpy(rp->pkt + fragmentable_offset + frag->offset + frag->ltrim,
frag->pkt + frag->data_offset, frag->data_len - frag->ltrim);
payload_len += frag->data_len - frag->ltrim;
}
}
BUG_ON(rp->ip6h == NULL);
rp->ip6h->s_ip6_plen = htons(payload_len);
rp->pktlen = pktlen + payload_len;
done:
SCMutexUnlock(&tracker->lock);
return rp;
}
/**
* \brief Timeout a tracker.
*
@ -829,6 +1056,52 @@ DefragTimeoutTracker(DefragContext *dc, Packet *p)
}
}
DefragTracker *
DefragGetTracker(DefragContext *dc, DefragTracker *lookup_key, Packet *p)
{
DefragTracker *tracker;
SCMutexLock(&dc->frag_table_lock);
tracker = HashListTableLookup(dc->frag_table, lookup_key,
sizeof(*lookup_key));
SCMutexUnlock(&dc->frag_table_lock);
if (tracker == NULL) {
SCMutexLock(&dc->tracker_pool_lock);
tracker = PoolGet(dc->tracker_pool);
if (tracker == NULL) {
/* Timeout trackers and try again. */
DefragTimeoutTracker(dc, p);
tracker = PoolGet(dc->tracker_pool);
}
SCMutexUnlock(&dc->tracker_pool_lock);
if (tracker == NULL) {
/* Report memory error - actually a pool allocation error. */
SCLogError(SC_ERR_MEM_ALLOC, "Defrag: Failed to allocate tracker.");
return NULL;
}
DefragTrackerReset(tracker);
tracker->family = lookup_key->family;
tracker->id = lookup_key->id;
tracker->src_addr = lookup_key->src_addr;
tracker->dst_addr = lookup_key->dst_addr;
/* XXX Do policy lookup. */
tracker->policy = dc->default_policy;
SCMutexLock(&dc->frag_table_lock);
if (HashListTableAdd(dc->frag_table, tracker, sizeof(*tracker)) != 0) {
/* Failed to add new tracker. */
SCMutexUnlock(&dc->frag_table_lock);
SCLogError(SC_ERR_MEM_ALLOC,
"Defrag: Failed to add new tracker to hash table.");
return NULL;
}
SCMutexUnlock(&dc->frag_table_lock);
}
return tracker;
}
/**
* \brief Entry point for IPv4 fragments.
*
@ -864,42 +1137,10 @@ Defrag4(ThreadVars *tv, DefragContext *dc, Packet *p)
lookup.id = IPV4_GET_IPID(p);
lookup.src_addr = p->src;
lookup.dst_addr = p->dst;
SCMutexLock(&dc->frag_table_lock);
tracker = HashListTableLookup(dc->frag_table, &lookup, sizeof(lookup));
SCMutexUnlock(&dc->frag_table_lock);
if (tracker == NULL) {
SCMutexLock(&dc->tracker_pool_lock);
tracker = PoolGet(dc->tracker_pool);
if (tracker == NULL) {
/* Timeout trackers and try again. */
DefragTimeoutTracker(dc, p);
tracker = PoolGet(dc->tracker_pool);
}
SCMutexUnlock(&dc->tracker_pool_lock);
if (tracker == NULL) {
/* Report memory error - actually a pool allocation error. */
SCLogError(SC_ERR_MEM_ALLOC, "Defrag: Failed to allocate tracker.");
return NULL;
}
DefragTrackerReset(tracker);
tracker->family = lookup.family;
tracker->id = lookup.id;
tracker->src_addr = lookup.src_addr;
tracker->dst_addr = lookup.dst_addr;
/* XXX Do policy lookup. */
tracker->policy = dc->default_policy;
SCMutexLock(&dc->frag_table_lock);
if (HashListTableAdd(dc->frag_table, tracker, sizeof(*tracker)) != 0) {
/* Failed to add new tracker. */
SCMutexUnlock(&dc->frag_table_lock);
SCLogError(SC_ERR_MEM_ALLOC,
"Defrag: Failed to add new tracker to hash table.");
return NULL;
}
SCMutexUnlock(&dc->frag_table_lock);
}
tracker = DefragGetTracker(dc, &lookup, p);
if (tracker == NULL)
return NULL;
if (!more_frags) {
tracker->seen_last = 1;
@ -925,13 +1166,42 @@ Defrag4(ThreadVars *tv, DefragContext *dc, Packet *p)
* NULL is returned.
*/
Packet *
Defrag6(DefragContext *dc, Packet *p)
Defrag6(ThreadVars *tv, DefragContext *dc, Packet *p)
{
uint16_t frag_offset;
uint8_t more_frags;
uint32_t id;
DefragTracker *tracker, lookup;
/* If no DefragContext was passed in, use the global one. Passing
* one in is primarily useful for unit tests. */
if (dc == NULL)
dc = defrag_context;
frag_offset = IPV6_EXTHDR_GET_FH_OFFSET(p);
more_frags = IPV6_EXTHDR_GET_FH_FLAG(p);
id = IPV6_EXTHDR_GET_FH_ID(p);
/* Create a lookup key. */
lookup.family = AF_INET6;
lookup.id = id;
lookup.src_addr = p->src;
lookup.dst_addr = p->dst;
tracker = DefragGetTracker(dc, &lookup, p);
if (tracker == NULL)
return NULL;
if (!more_frags) {
tracker->seen_last = 1;
}
Defrag6InsertFrag(dc, tracker, p);
if (tracker->seen_last) {
Packet *rp = Defrag6Reassemble(tv, dc, tracker, p);
return rp;
}
return NULL;
}
@ -1023,6 +1293,64 @@ error:
return NULL;
}
static Packet *
IPV6BuildTestPacket(uint32_t id, uint16_t off, int mf, const char content,
int content_len)
{
Packet *p = NULL;
p = calloc(1, sizeof(*p));
if (p == NULL)
return NULL;
gettimeofday(&p->ts, NULL);
p->ip6h = (IPV6Hdr *)p->pkt;
IPV6_SET_RAW_VER(p->ip6h, 6);
p->ip6h->s_ip6_nxt = 44;
p->ip6h->s_ip6_hlim = 2;
/* Source and dest address - very bogus addresses. */
p->ip6h->ip6_src[0] = 0x01010101;
p->ip6h->ip6_src[1] = 0x01010101;
p->ip6h->ip6_src[2] = 0x01010101;
p->ip6h->ip6_src[3] = 0x01010101;
p->ip6h->ip6_dst[0] = 0x02020202;
p->ip6h->ip6_dst[1] = 0x02020202;
p->ip6h->ip6_dst[2] = 0x02020202;
p->ip6h->ip6_dst[3] = 0x02020202;
/* Fragmentation header. */
p->ip6eh.ip6fh = (IPV6FragHdr *)(p->pkt + sizeof(IPV6Hdr));
p->ip6eh.ip6fh->ip6fh_nxt = IPPROTO_ICMP;
p->ip6eh.ip6fh->ip6fh_ident = htonl(id);
p->ip6eh.ip6fh->ip6fh_offlg = htons((off << 3) | mf);
memset(p->pkt + sizeof(IPV6Hdr) + sizeof(IPV6FragHdr), content,
content_len);
p->pktlen = sizeof(IPV6Hdr) + sizeof(IPV6FragHdr) + content_len;
p->ip6h->s_ip6_plen = htons(sizeof(IPV6FragHdr) + content_len);
SET_IPV6_SRC_ADDR(p, &p->src);
SET_IPV6_DST_ADDR(p, &p->dst);
/* Self test. */
IPV6_CACHE_INIT(p);
if (IPV6_GET_VER(p) != 6)
goto error;
if (IPV6_GET_NH(p) != 44)
goto error;
if (IPV6_GET_PLEN(p) != sizeof(IPV6FragHdr) + content_len)
goto error;
return p;
error:
fprintf(stderr, "Error building test packet.\n");
if (p != NULL)
free(p);
return NULL;
}
/**
* Test the simplest possible re-assembly scenario. All packet in
* order and no overlaps.
@ -1096,6 +1424,79 @@ end:
return ret;
}
/**
* Test the simplest possible re-assembly scenario. All packet in
* order and no overlaps.
*/
static int
IPV6DefragInOrderSimpleTest(void)
{
DefragContext *dc = NULL;
Packet *p1 = NULL, *p2 = NULL, *p3 = NULL;
Packet *reassembled = NULL;
int id = 12;
int i;
int ret = 0;
DefragInit();
dc = DefragContextNew();
if (dc == NULL)
goto end;
p1 = IPV6BuildTestPacket(id, 0, 1, 'A', 8);
if (p1 == NULL)
goto end;
p2 = IPV6BuildTestPacket(id, 1, 1, 'B', 8);
if (p2 == NULL)
goto end;
p3 = IPV6BuildTestPacket(id, 2, 0, 'C', 3);
if (p3 == NULL)
goto end;
if (Defrag6(NULL, dc, p1) != NULL)
goto end;
if (Defrag6(NULL, dc, p2) != NULL)
goto end;
reassembled = Defrag6(NULL, dc, p3);
if (reassembled == NULL)
goto end;
/* 40 bytes in we should find 8 bytes of A. */
for (i = 40; i < 40 + 8; i++) {
if (reassembled->pkt[i] != 'A')
goto end;
}
/* 28 bytes in we should find 8 bytes of B. */
for (i = 48; i < 48 + 8; i++) {
if (reassembled->pkt[i] != 'B')
goto end;
}
/* And 36 bytes in we should find 3 bytes of C. */
for (i = 56; i < 56 + 3; i++) {
if (reassembled->pkt[i] != 'C')
goto end;
}
ret = 1;
end:
if (dc != NULL)
DefragContextDestroy(dc);
if (p1 != NULL)
free(p1);
if (p2 != NULL)
free(p2);
if (p3 != NULL)
free(p3);
if (reassembled != NULL)
free(reassembled);
DefragDestroy();
return ret;
}
static int
DefragDoSturgesNovakTest(int policy, u_char *expected, size_t expected_len)
{
@ -1205,6 +1606,115 @@ end:
return ret;
}
static int
IPV6DefragDoSturgesNovakTest(int policy, u_char *expected, size_t expected_len)
{
int i;
int ret = 0;
DefragContext *dc = NULL;
DefragInit();
/*
* Build the packets.
*/
int id = 1;
Packet *packets[17];
memset(packets, 0x00, sizeof(packets));
/*
* Original fragments.
*/
/* A*24 at 0. */
packets[0] = IPV6BuildTestPacket(id, 0, 1, 'A', 24);
/* B*15 at 32. */
packets[1] = IPV6BuildTestPacket(id, 32 >> 3, 1, 'B', 16);
/* C*24 at 48. */
packets[2] = IPV6BuildTestPacket(id, 48 >> 3, 1, 'C', 24);
/* D*8 at 80. */
packets[3] = IPV6BuildTestPacket(id, 80 >> 3, 1, 'D', 8);
/* E*16 at 104. */
packets[4] = IPV6BuildTestPacket(id, 104 >> 3, 1, 'E', 16);
/* F*24 at 120. */
packets[5] = IPV6BuildTestPacket(id, 120 >> 3, 1, 'F', 24);
/* G*16 at 144. */
packets[6] = IPV6BuildTestPacket(id, 144 >> 3, 1, 'G', 16);
/* H*16 at 160. */
packets[7] = IPV6BuildTestPacket(id, 160 >> 3, 1, 'H', 16);
/* I*8 at 176. */
packets[8] = IPV6BuildTestPacket(id, 176 >> 3, 1, 'I', 8);
/*
* Overlapping subsequent fragments.
*/
/* J*32 at 8. */
packets[9] = IPV6BuildTestPacket(id, 8 >> 3, 1, 'J', 32);
/* K*24 at 48. */
packets[10] = IPV6BuildTestPacket(id, 48 >> 3, 1, 'K', 24);
/* L*24 at 72. */
packets[11] = IPV6BuildTestPacket(id, 72 >> 3, 1, 'L', 24);
/* M*24 at 96. */
packets[12] = IPV6BuildTestPacket(id, 96 >> 3, 1, 'M', 24);
/* N*8 at 128. */
packets[13] = IPV6BuildTestPacket(id, 128 >> 3, 1, 'N', 8);
/* O*8 at 152. */
packets[14] = IPV6BuildTestPacket(id, 152 >> 3, 1, 'O', 8);
/* P*8 at 160. */
packets[15] = IPV6BuildTestPacket(id, 160 >> 3, 1, 'P', 8);
/* Q*16 at 176. */
packets[16] = IPV6BuildTestPacket(id, 176 >> 3, 0, 'Q', 16);
dc = DefragContextNew();
if (dc == NULL)
goto end;
dc->default_policy = policy;
/* Send all but the last. */
for (i = 0; i < 16; i++) {
Packet *tp = Defrag6(NULL, dc, packets[i]);
if (tp != NULL) {
free(tp);
goto end;
}
}
/* And now the last one. */
Packet *reassembled = Defrag6(NULL, dc, packets[16]);
if (reassembled == NULL)
goto end;
if (memcmp(reassembled->pkt + 40, expected, expected_len) != 0)
goto end;
free(reassembled);
ret = 1;
end:
if (dc != NULL)
DefragContextDestroy(dc);
for (i = 0; i < 17; i++) {
free(packets[i]);
}
DefragDestroy();
return ret;
}
static int
DefragSturgesNovakBsdTest(void)
{
@ -1239,6 +1749,40 @@ DefragSturgesNovakBsdTest(void)
return DefragDoSturgesNovakTest(POLICY_BSD, expected, sizeof(expected));
}
static int
IPV6DefragSturgesNovakBsdTest(void)
{
/* Expected data. */
u_char expected[] = {
"AAAAAAAA"
"AAAAAAAA"
"AAAAAAAA"
"JJJJJJJJ"
"JJJJJJJJ"
"BBBBBBBB"
"CCCCCCCC"
"CCCCCCCC"
"CCCCCCCC"
"LLLLLLLL"
"LLLLLLLL"
"LLLLLLLL"
"MMMMMMMM"
"MMMMMMMM"
"MMMMMMMM"
"FFFFFFFF"
"FFFFFFFF"
"FFFFFFFF"
"GGGGGGGG"
"GGGGGGGG"
"HHHHHHHH"
"HHHHHHHH"
"IIIIIIII"
"QQQQQQQQ"
};
return IPV6DefragDoSturgesNovakTest(POLICY_BSD, expected, sizeof(expected));
}
static int
DefragSturgesNovakLinuxTest(void)
{
@ -1506,6 +2050,11 @@ DefragRegisterTests(void)
DefragSturgesNovakLastTest, 1);
UtRegisterTest("DefragTimeoutTest",
DefragTimeoutTest, 1);
UtRegisterTest("IPV6DefragInOrderSimpleTest",
IPV6DefragInOrderSimpleTest, 1);
UtRegisterTest("IPV6DefragSturgesNovakBsdTest",
IPV6DefragSturgesNovakBsdTest, 1);
#endif /* UNITTESTS */
}

@ -15,7 +15,7 @@ typedef struct _DefragContext DefragContext;
void DefragInit(void);
Packet *Defrag4(ThreadVars *, DefragContext *, Packet *);
Packet *Defrag6(DefragContext *, Packet *);
Packet *Defrag6(ThreadVars *, DefragContext *, Packet *);
void DefragRegisterTests(void);
#endif /* __DEFRAG_H__ */

Loading…
Cancel
Save