From 2e4d1ef5fe1f39cfd261b43c5962282701dcffff Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Mon, 1 Feb 2010 11:39:59 -0800 Subject: [PATCH] Fix issue 71. The insert and re-assemble need to be done under the same tracker lock. --- src/defrag.c | 495 +++++++++++++++++++++++++++++----------------------------- 1 files changed, 244 insertions(+), 251 deletions(-) diff --git a/src/defrag.c b/src/defrag.c index 88476a5..d0a0df8 100644 --- a/src/defrag.c +++ b/src/defrag.c @@ -88,7 +88,6 @@ typedef struct _DefragContext { time_t timeout; /**< Default timeout. */ uint8_t default_policy; /**< Default policy. */ - } DefragContext; /** @@ -137,8 +136,8 @@ typedef struct _DefragTracker { struct timeval timeout; /**< When this tracker will timeout. */ - uint8_t family; /**< Address family for this tracker, AF_INET or - * AF_INET6. */ + uint8_t af; /**< Address family for this tracker, AF_INET or + * AF_INET6. */ uint32_t id; /**< IP ID for this tracker. 32 bits for IPv6, 16 * for IPv4. */ @@ -197,13 +196,13 @@ DefragHashFunc(HashListTable *ht, void *data, uint16_t datalen) DefragTracker *p = (DefragTracker *)data; uint32_t key; - if (p->family == AF_INET) { - key = (defrag_hash_rand + p->family + + if (p->af == AF_INET) { + key = (defrag_hash_rand + p->af + p->src_addr.addr_data32[0] + p->dst_addr.addr_data32[0]) % defrag_hash_size; } - else if (p->family == AF_INET6) { - key = (defrag_hash_rand + p->family + + else if (p->af == AF_INET6) { + key = (defrag_hash_rand + p->af + p->src_addr.addr_data32[0] + p->src_addr.addr_data32[1] + p->src_addr.addr_data32[2] + p->src_addr.addr_data32[3] + p->dst_addr.addr_data32[0] + p->dst_addr.addr_data32[1] + @@ -227,7 +226,7 @@ DefragHashCompare(void *a, uint16_t a_len, void *b, uint16_t b_len) DefragTracker *dta = (DefragTracker *)a; DefragTracker *dtb = (DefragTracker *)b; - if (dta->family != dtb->family) + if (dta->af != dtb->af) return 0; else if (dta->id != dtb->id) return 0; @@ -413,7 +412,7 @@ DefragContextNew(void) exit(EXIT_FAILURE); } if (SCMutexInit(&dc->tracker_pool_lock, NULL) != 0) { - SCLogError(SC_ERR_MEM_ALLOC, + SCLogError(SC_ERR_MUTEX, "Defrag: Failed to initialize tracker pool mutex."); exit(EXIT_FAILURE); } @@ -429,7 +428,7 @@ DefragContextNew(void) exit(EXIT_FAILURE); } if (SCMutexInit(&dc->frag_pool_lock, NULL) != 0) { - SCLogError(SC_ERR_MEM_ALLOC, + SCLogError(SC_ERR_MUTEX, "Defrag: Failed to initialize frag pool mutex."); exit(EXIT_FAILURE); } @@ -475,218 +474,6 @@ DefragContextDestroy(DefragContext *dc) free(dc); } -/** - * Insert a new IPv4/IPv6 fragment into a tracker. - * - * \todo Allocate packet buffers from a pool. - */ -static void -DefragInsertFrag(DefragContext *dc, DefragTracker *tracker, Packet *p) -{ - int ltrim = 0; - - uint8_t more_frags; - uint16_t frag_offset; - - /* IPv4 header length - IPv4 only. */ - uint16_t hlen = 0; - - /* This is the offset of the start of the data in the packet that - * falls after the IP header. */ - uint16_t data_offset; - - /* The length of the (fragmented) data. This is the length of the - * data that falls after the IP header. */ - uint16_t data_len; - - /* Where the fragment ends. */ - uint16_t frag_end; - - /* Offset in the packet to the IPv6 header. */ - uint16_t ip_hdr_offset; - - /* Offset in the packet to the IPv6 frag header. IPv6 only. */ - uint16_t frag_hdr_offset = 0; - - if (tracker->family == AF_INET) { - more_frags = IPV4_GET_MF(p); - frag_offset = IPV4_GET_IPOFFSET(p) << 3; - hlen = IPV4_GET_HLEN(p); - data_offset = (uint8_t *)p->ip4h + hlen - p->pkt; - data_len = IPV4_GET_IPLEN(p) - hlen; - frag_end = frag_offset + data_len; - ip_hdr_offset = (uint8_t *)p->ip4h - p->pkt; - - /* Ignore fragment if the end of packet extends past the - * maximum size of a packet. */ - if (IPV4_HEADER_LEN + frag_offset + data_len > IPV4_MAXPACKET_LEN) { - /** \todo Perhaps log something? */ - return; - } - } - else if (tracker->family == AF_INET6) { - more_frags = IPV6_EXTHDR_GET_FH_FLAG(p); - frag_offset = IPV6_EXTHDR_GET_FH_OFFSET(p); - data_offset = (uint8_t *)p->ip6eh.ip6fh + sizeof(IPV6FragHdr) - p->pkt; - data_len = IPV6_GET_PLEN(p) - ( - ((uint8_t *)p->ip6eh.ip6fh + sizeof(IPV6FragHdr)) - - ((uint8_t *)p->ip6h + sizeof(IPV6Hdr))); - frag_end = frag_offset + data_len; - ip_hdr_offset = (uint8_t *)p->ip6h - p->pkt; - frag_hdr_offset = (uint8_t *)p->ip6eh.ip6fh - p->pkt; - - /* Ignore fragment if the end of packet extends past the - * maximum size of a packet. */ - if (frag_offset + data_len > IPV6_MAXPACKET) { - /** \todo Perhaps log something? */ - return; - } - } - else { - /* Abort - should not happen. */ - SCLogError(SC_INVALID_ARGUMENT, "Invalid address family, aborting."); - exit(EXIT_FAILURE); - } - - /* 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 = NULL, *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; - } - if ((frag_offset < prev->offset) && - (frag_end >= prev->offset + prev->data_len)) { - prev->skip = 1; - } - goto insert; - } - break; - case POLICY_LINUX: - 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; - } - if ((frag_offset < prev->offset) && - (frag_end >= prev->offset + prev->data_len)) { - prev->skip = 1; - } - goto insert; - } - break; - case POLICY_WINDOWS: - if (frag_offset < prev->offset + prev->data_len) { - if (frag_offset >= prev->offset) { - ltrim = prev->offset + prev->data_len - frag_offset; - } - if ((frag_offset < prev->offset) && - (frag_end > prev->offset + prev->data_len)) { - prev->skip = 1; - } - goto insert; - } - break; - case POLICY_SOLARIS: - if (frag_offset < prev->offset + prev->data_len) { - if (frag_offset >= prev->offset) { - ltrim = prev->offset + prev->data_len - frag_offset; - } - if ((frag_offset < prev->offset) && - (frag_end >= prev->offset + prev->data_len)) { - prev->skip = 1; - } - goto insert; - } - break; - case POLICY_FIRST: - if ((frag_offset >= prev->offset) && - (frag_end <= prev->offset + prev->data_len)) - goto done; - if (frag_offset < prev->offset) - goto insert; - if (frag_offset < prev->offset + prev->data_len) { - ltrim = prev->offset + prev->data_len - frag_offset; - goto insert; - } - break; - case POLICY_LAST: - if (frag_offset <= prev->offset) { - if (frag_end > prev->offset) - prev->ltrim = frag_end - prev->offset; - 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->hlen = hlen; - new->offset = frag_offset + ltrim; - new->data_offset = data_offset; - new->data_len = data_len - ltrim; - new->ip_hdr_offset = ip_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); - } - - if (!more_frags) { - tracker->seen_last = 1; - } - -done: - SCMutexUnlock(&tracker->lock); -} /** * Attempt to re-assemble a packet. @@ -703,9 +490,6 @@ Defrag4Reassemble(ThreadVars *tv, DefragContext *dc, DefragTracker *tracker, 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; @@ -806,7 +590,6 @@ remove_tracker: SCMutexUnlock(&dc->tracker_pool_lock); done: - SCMutexUnlock(&tracker->lock); return rp; } @@ -825,9 +608,6 @@ Defrag6Reassemble(ThreadVars *tv, DefragContext *dc, DefragTracker *tracker, 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; @@ -919,11 +699,233 @@ remove_tracker: SCMutexUnlock(&dc->tracker_pool_lock); done: - SCMutexUnlock(&tracker->lock); return rp; } /** + * Insert a new IPv4/IPv6 fragment into a tracker. + * + * \todo Allocate packet buffers from a pool. + */ +static Packet * +DefragInsertFrag(ThreadVars *tv, DefragContext *dc, DefragTracker *tracker, + Packet *p) +{ + Packet *r = NULL; + int ltrim = 0; + + uint8_t more_frags; + uint16_t frag_offset; + + /* IPv4 header length - IPv4 only. */ + uint16_t hlen = 0; + + /* This is the offset of the start of the data in the packet that + * falls after the IP header. */ + uint16_t data_offset; + + /* The length of the (fragmented) data. This is the length of the + * data that falls after the IP header. */ + uint16_t data_len; + + /* Where the fragment ends. */ + uint16_t frag_end; + + /* Offset in the packet to the IPv6 header. */ + uint16_t ip_hdr_offset; + + /* Offset in the packet to the IPv6 frag header. IPv6 only. */ + uint16_t frag_hdr_offset = 0; + + if (tracker->af == AF_INET) { + more_frags = IPV4_GET_MF(p); + frag_offset = IPV4_GET_IPOFFSET(p) << 3; + hlen = IPV4_GET_HLEN(p); + data_offset = (uint8_t *)p->ip4h + hlen - p->pkt; + data_len = IPV4_GET_IPLEN(p) - hlen; + frag_end = frag_offset + data_len; + ip_hdr_offset = (uint8_t *)p->ip4h - p->pkt; + + /* Ignore fragment if the end of packet extends past the + * maximum size of a packet. */ + if (IPV4_HEADER_LEN + frag_offset + data_len > IPV4_MAXPACKET_LEN) { + /** \todo Perhaps log something? */ + return NULL;; + } + } + else if (tracker->af == AF_INET6) { + more_frags = IPV6_EXTHDR_GET_FH_FLAG(p); + frag_offset = IPV6_EXTHDR_GET_FH_OFFSET(p); + data_offset = (uint8_t *)p->ip6eh.ip6fh + sizeof(IPV6FragHdr) - p->pkt; + data_len = IPV6_GET_PLEN(p) - ( + ((uint8_t *)p->ip6eh.ip6fh + sizeof(IPV6FragHdr)) - + ((uint8_t *)p->ip6h + sizeof(IPV6Hdr))); + frag_end = frag_offset + data_len; + ip_hdr_offset = (uint8_t *)p->ip6h - p->pkt; + frag_hdr_offset = (uint8_t *)p->ip6eh.ip6fh - p->pkt; + + /* Ignore fragment if the end of packet extends past the + * maximum size of a packet. */ + if (frag_offset + data_len > IPV6_MAXPACKET) { + /** \todo Perhaps log something? */ + return NULL; + } + } + else { + /* Abort - should not happen. */ + SCLogWarning(SC_INVALID_ARGUMENT, "Invalid address family, aborting."); + return NULL; + } + + /* 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 = NULL, *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; + } + if ((frag_offset < prev->offset) && + (frag_end >= prev->offset + prev->data_len)) { + prev->skip = 1; + } + goto insert; + } + break; + case POLICY_LINUX: + 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; + } + if ((frag_offset < prev->offset) && + (frag_end >= prev->offset + prev->data_len)) { + prev->skip = 1; + } + goto insert; + } + break; + case POLICY_WINDOWS: + if (frag_offset < prev->offset + prev->data_len) { + if (frag_offset >= prev->offset) { + ltrim = prev->offset + prev->data_len - frag_offset; + } + if ((frag_offset < prev->offset) && + (frag_end > prev->offset + prev->data_len)) { + prev->skip = 1; + } + goto insert; + } + break; + case POLICY_SOLARIS: + if (frag_offset < prev->offset + prev->data_len) { + if (frag_offset >= prev->offset) { + ltrim = prev->offset + prev->data_len - frag_offset; + } + if ((frag_offset < prev->offset) && + (frag_end >= prev->offset + prev->data_len)) { + prev->skip = 1; + } + goto insert; + } + break; + case POLICY_FIRST: + if ((frag_offset >= prev->offset) && + (frag_end <= prev->offset + prev->data_len)) + goto done; + if (frag_offset < prev->offset) + goto insert; + if (frag_offset < prev->offset + prev->data_len) { + ltrim = prev->offset + prev->data_len - frag_offset; + goto insert; + } + break; + case POLICY_LAST: + if (frag_offset <= prev->offset) { + if (frag_end > prev->offset) + prev->ltrim = frag_end - prev->offset; + 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->hlen = hlen; + new->offset = frag_offset + ltrim; + new->data_offset = data_offset; + new->data_len = data_len - ltrim; + new->ip_hdr_offset = ip_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); + } + + if (!more_frags) { + tracker->seen_last = 1; + } + + if (tracker->seen_last) { + if (tracker->af == AF_INET) + r = Defrag4Reassemble(tv, dc, tracker, p); + else if (tracker->af == AF_INET6) + r = Defrag6Reassemble(tv, dc, tracker, p); + } + +done: + SCMutexUnlock(&tracker->lock); + return r; +} + +/** * \brief Timeout a tracker. * * Called when we fail to get a tracker from the pool. The first @@ -965,7 +967,6 @@ DefragGetTracker(DefragContext *dc, DefragTracker *lookup_key, Packet *p) 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); @@ -978,10 +979,10 @@ DefragGetTracker(DefragContext *dc, DefragTracker *lookup_key, Packet *p) if (tracker == NULL) { /* Report memory error - actually a pool allocation error. */ SCLogError(SC_ERR_MEM_ALLOC, "Defrag: Failed to allocate tracker."); - return NULL; + goto done; } DefragTrackerReset(tracker); - tracker->family = lookup_key->family; + tracker->af = lookup_key->af; tracker->id = lookup_key->id; tracker->src_addr = lookup_key->src_addr; tracker->dst_addr = lookup_key->dst_addr; @@ -989,17 +990,19 @@ DefragGetTracker(DefragContext *dc, DefragTracker *lookup_key, Packet *p) /* 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; + SCMutexLock(&dc->tracker_pool_lock); + PoolReturn(dc->tracker_pool, tracker); + SCMutexUnlock(&dc->tracker_pool_lock); + goto done; } - SCMutexUnlock(&dc->frag_table_lock); } +done: + SCMutexUnlock(&dc->frag_table_lock); return tracker; } @@ -1049,7 +1052,7 @@ Defrag(ThreadVars *tv, DefragContext *dc, Packet *p) } /* Create a lookup key. */ - lookup.family = af; + lookup.af = af; lookup.id = id; lookup.src_addr = p->src; lookup.dst_addr = p->dst; @@ -1058,17 +1061,7 @@ Defrag(ThreadVars *tv, DefragContext *dc, Packet *p) if (tracker == NULL) return NULL; - DefragInsertFrag(dc, tracker, p); - if (tracker->seen_last) { - Packet *rp = NULL; - if (af == AF_INET) - rp = Defrag4Reassemble(tv, dc, tracker, p); - else if (af == AF_INET6) - rp = Defrag6Reassemble(tv, dc, tracker, p); - return rp; - } - - return NULL; + return DefragInsertFrag(tv, dc, tracker, p); } void @@ -2342,7 +2335,7 @@ DefragIPv4NoDataTest(void) DefragContext *dc = NULL; Packet *p = NULL; int id = 12; - int ret; + int ret = 0; DefragInit(); @@ -2380,7 +2373,7 @@ DefragIPv4TooLargeTest(void) { DefragContext *dc = NULL; Packet *p = NULL; - int ret; + int ret = 0; DefragInit(); -- 1.6.6