/* * Copyright (c) 2017, Peter Haag * Copyright (c) 2016, Peter Haag * Copyright (c) 2014, Peter Haag * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the author nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Author: peter * */ #include "config.h" #include #include #include #include #include #include #include #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #include #include #include #include #include "util.h" #include "rbtree.h" #include "ipfrag.h" #ifndef DEVEL # define dbg_printf(...) /* printf(__VA_ARGS__) */ #else # define dbg_printf(...) printf(__VA_ARGS__) #endif #define KEYLEN (offsetof(IPFragNode_t,data_size) - offsetof(IPFragNode_t, src_addr)) static int IPFragNodeCMP(struct IPFragNode *e1, struct IPFragNode *e2); static struct IPFragNode *New_frag_node(void); static void Free_node(struct IPFragNode *node, int free_data); static void Remove_node(struct IPFragNode *node); // Insert the IP RB tree code here RB_GENERATE(IPFragTree, IPFragNode, entry, IPFragNodeCMP); static IPFragTree_t *IPFragTree; static uint32_t NumFragments; static int IPFragNodeCMP(struct IPFragNode *e1, struct IPFragNode *e2) { uint32_t *a = &e1->src_addr; uint32_t *b = &e2->src_addr; int i; // 2 x sizeof(uint32_t) (8) + frag_offset == 12 i = memcmp((void *)a, (void *)b, KEYLEN ); return i; } // End of IPFragNodeCMP static struct IPFragNode *New_frag_node(void) { struct IPFragNode *node; node = malloc(sizeof(struct IPFragNode)); if ( !node ) { LogError("malloc() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) ); return NULL; } memset((void *)node, 0, sizeof(struct IPFragNode)); node->data = malloc(IP_MAXPACKET); if ( !node->data ) { LogError("malloc() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) ); free(node); return NULL; } memset(node->data, 0, IP_MAXPACKET); node->eod = node->data; node->data_size = 0; node->holes = malloc(sizeof(hole_t)); if ( !node->holes ) { LogError("malloc() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) ); free(node); return NULL; } node->holes->next = NULL; node->holes->first = 0; node->holes->last = IP_MAXPACKET; NumFragments++; return node; } // End of New_frag_node static void Free_node(struct IPFragNode *node, int free_data) { hole_t *hole, *h; hole = node->holes; while (hole) { h = hole->next; free(hole); hole = h; } if (free_data) free(node->data); free(node); NumFragments--; } // End of Free_node static void Remove_node(struct IPFragNode *node) { struct IPFragNode *n; n = RB_REMOVE(IPFragTree, IPFragTree, node); if ( n ) { Free_node(n, 0); } // else - node not in tree } // End of Remove_node int IPFragTree_init(void) { IPFragTree = malloc(sizeof(IPFragTree_t)); if ( !IPFragTree ) { LogError("malloc() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) ); return 0; } RB_INIT(IPFragTree); NumFragments = 0; dbg_printf("IPFrag key len: %lu\n", KEYLEN); return 1; } // End of IPFragTree_init void IPFragTree_free(void) { struct IPFragNode *node, *nxt; // Dump all incomplete flows to the file for (node = RB_MIN(IPFragTree, IPFragTree); node != NULL; node = nxt) { nxt = RB_NEXT(IPFragTree, IPFragTree, node); RB_REMOVE(IPFragTree, IPFragTree, node); Free_node(node, 1); } free(IPFragTree); IPFragTree = NULL; NumFragments = 0; } // End of IPFragTree_free void *IPFrag_tree_Update(uint32_t src, uint32_t dst, uint32_t ident, uint32_t *length, uint32_t ip_off, void *data) { struct IPFragNode FindNode, *n; hole_t *hole, *h, *hole_parent; uint16_t more_fragments, first, last, max; int found_hole; FindNode.src_addr = src; FindNode.dst_addr = dst; FindNode.ident = ident; n = RB_FIND(IPFragTree, IPFragTree, &FindNode); if ( !n ) { n = New_frag_node(); n->src_addr = src; n->dst_addr = dst; n->ident = ident; if ( RB_INSERT(IPFragTree, IPFragTree, n) ) { // must never happen LogError("Node insert returned existing node - Software error in %s line %d", __FILE__, __LINE__); } } hole = n->holes; hole_parent = NULL; first = (ip_off & IP_OFFMASK) << 3; more_fragments = (ip_off & IP_MF) != 0 ? 1 : 0; last = first + *length - 1; if ( last > IP_MAXPACKET ) { LogError("Fragment assembly error: last > IP_MAXPACKET"); LogError("Fragment assembly: first: %u, last: %u, MF: %u\n", first, last, more_fragments); return NULL; } // last fragment - sets max offset found_hole = 0; max = more_fragments == 0 ? last : 0; dbg_printf("Fragment assembly: first: %u, last: %u, MF: %u\n", first, last, more_fragments); while (hole) { uint16_t hole_last; if ( max ) { dbg_printf("max in last fragment: %u\n", max); // last fragment offset/length if ( hole->last == IP_MAXPACKET ) { // last fragment has max size if ( max > hole->first ) { dbg_printf("set max of last fragment: %u\n", max); hole->last = max; } else { LogError("last fragment offset error - teardrop attack??"); } } else { // last fragment must always be max offset if ( max < hole->last ) { LogError("last fragment offset error - teardrop attack??"); } } } dbg_printf("Check Hole: first: %u, last: %u\n", hole->first, hole->last); if ( first > hole->last ) { hole_parent = hole; hole = hole->next; dbg_printf("Fragment right outside hole\n"); continue; } if ( last < hole->first ) { hole_parent = hole; hole = hole->next; dbg_printf("Fragment left outside hole\n"); continue; } // fragment fits into hole found_hole = 1; if ( last > n->data_size ) n->data_size = last; hole_last = hole->last; if ( first == hole->first ) { dbg_printf("Fragment matches first\n"); // fragment fits at beginning of hole if ( last == hole->last ) { dbg_printf("Fragment matches last\n"); // fragment fits completly into hole - delete hole if ( hole_parent ) { hole_parent->next = hole->next; } else { n->holes = NULL; } free(hole); hole = NULL; } else { // fragment smaller than hole dbg_printf("Fragment smaller than hole\n"); hole->first = last+1; } } else { // fragment start within hole dbg_printf("Fragment inside hole\n"); hole->last = first - 1; if ( last < hole_last ) { // fragment ends within hole - add another hole h = malloc(sizeof(hole_t)); if ( !h ) { LogError("malloc() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) ); return NULL; } h->first = last + 1; h->last = hole_last; h->next = n->holes; n->holes = h; } } memcpy(n->data + first, data, *length); break; } if ( !found_hole ) LogError("No space - Fragment overlap: first: %u, last: %u\n", first, last); if ( n->holes == NULL ) { void *data = n->data; n->data_size++; *length = n->data_size; Remove_node(n); dbg_printf("Datagramm complete - size: %u\n", n->data_size); return data; } else { return NULL; } } // End of IPFrag_tree_Update uint32_t IPFragEntries() { return NumFragments; } // End of IPFragEntries