/* * netsniff-ng - the packet sniffing beast * Copyright 2011 Daniel Borkmann, rewritten * Copyright 1991-2007 Kawahara Lab., Kyoto University * Copyright 2000-2005 Shikano Lab., Nara Institute of Science and Technology * Copyright 2005-2007 Julius project team, Nagoya Institute of Technology * All rights reserved * Subject to the GPL, version 2. */ #include #include #include #include "patricia.h" #include "built_in.h" #include "xmalloc.h" static const unsigned char mbit[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; static inline int testbit(char *str, size_t slen, int bitplace) { int maskptr; if ((maskptr = bitplace >> 3) > slen) return 0; return (str[maskptr] & mbit[bitplace & 7]); } static inline int testbit_max(char *str, int bitplace, int maxbitplace) { if (bitplace >= maxbitplace) return 0; return (str[bitplace >> 3] & mbit[bitplace & 7]); } static int where_the_bit_differ(char *str1, size_t l1, char *str2, size_t l2) { int p = 0, bitloc; while (str1[p] == str2[p]) p++; bitloc = p * 8; while (testbit(str1, l1, bitloc) == testbit(str2, l2, bitloc)) bitloc++; return bitloc; } static struct patricia_node *new_node(void) { struct patricia_node *n = xzmalloc(sizeof(*n)); n->l = n->r = NULL; return n; } static void free_node(struct patricia_node *n) { free(n->key); free(n->addr); xfree(n); } void ptree_display(struct patricia_node *node, int level) { int i; for (i = 0; i < level; ++i) printf("-"); if (node->l == NULL && node->r == NULL) printf("leaf: (%s -> %d)\n", (char *) node->key, node->value.data); else { printf("thres: %d\n", node->value.thres_bit); if (node->l != NULL) ptree_display(node->l, level + 1); if (node->r != NULL) ptree_display(node->r, level + 1); } } void ptree_get_key(int data, struct patricia_node *node, struct patricia_node **wanted) { if (!node) return; if (node->l == NULL && node->r == NULL) { if (node->value.data == data) (*wanted) = node; } else { if (node->l != NULL) ptree_get_key(data, node->l, wanted); if (node->r != NULL) ptree_get_key(data, node->r, wanted); } } void ptree_get_key_addr(struct sockaddr_storage *addr, size_t alen, struct patricia_node *node, struct patricia_node **wanted) { if (!node) return; if (node->l == NULL && node->r == NULL) { if (!memcmp(node->addr, addr, node->alen)) (*wanted) = node; } else { if (node->l != NULL) ptree_get_key_addr(addr, alen, node->l, wanted); if (node->r != NULL) ptree_get_key_addr(addr, alen, node->r, wanted); } } static int ptree_search_data_r(struct patricia_node *node, char *str, size_t slen, struct sockaddr_storage *addr, size_t *alen, int maxbitplace) { if (node->l == NULL && node->r == NULL) { if (node->addr && addr) memcpy(addr, node->addr, node->alen); (*alen) = node->alen; return node->value.data; } if (testbit_max(str, node->value.thres_bit, maxbitplace) != 0) return ptree_search_data_r(node->r, str, slen, addr, alen, maxbitplace); else return ptree_search_data_r(node->l, str, slen, addr, alen, maxbitplace); } static int *ptree_search_data_r_p(struct patricia_node *node, char *str, size_t slen, int maxbitplace) { if (node->l == NULL && node->r == NULL) return &(node->value.data); if (testbit_max(str, node->value.thres_bit, maxbitplace) != 0) return ptree_search_data_r_p(node->r, str, slen, maxbitplace); else return ptree_search_data_r_p(node->l, str, slen, maxbitplace); } static int ptree_search_data_r_x(struct patricia_node *node, char *str, size_t slen, struct sockaddr_storage *addr, size_t *alen, int maxbitplace) { if (node->l == NULL && node->r == NULL) { if (node->klen == slen && !memcmp(str, node->key, node->klen)) { if (node->addr && addr) memcpy(addr, node->addr, node->alen); (*alen) = node->alen; return node->value.data; } else return -ENOENT; } if (testbit_max(str, node->value.thres_bit, maxbitplace) != 0) return ptree_search_data_r_x(node->r, str, slen, addr, alen, maxbitplace); else return ptree_search_data_r_x(node->l, str, slen, addr, alen, maxbitplace); } int ptree_search_data_nearest(void *str, size_t sstr, struct sockaddr_storage *addr, size_t *alen, struct patricia_node *root) { if (!root) return -ENOENT; return ptree_search_data_r(root, str, sstr, addr, alen, sstr * 8); } static int *ptree_search_data_nearest_ptr(char *str, size_t sstr, struct patricia_node *root) { return ptree_search_data_r_p(root, str, sstr, sstr * 8); } int ptree_search_data_exact(void *str, size_t sstr, struct sockaddr_storage *addr, size_t *alen, struct patricia_node *root) { if (!root) return -ENOENT; return ptree_search_data_r_x(root, str, sstr, addr, alen, sstr * 8); } static struct patricia_node *ptree_make_root_node(char *str, size_t sstr, int data, struct sockaddr_storage *addr, size_t alen) { struct patricia_node *n = new_node(); n->value.data = data; n->key = xmemdupz(str, sstr); n->klen = sstr; if (addr) n->addr = xmemdupz(addr, alen); else n->addr = NULL; n->alen = alen; return n; } static void ptree_add_entry_at(char *str, size_t slen, int bitloc, int data, struct sockaddr_storage *addr, size_t alen, struct patricia_node **parentlink) { struct patricia_node *node = (*parentlink); if (node->value.thres_bit > bitloc || (node->l == NULL && node->r == NULL)) { struct patricia_node *newleaf, *newbranch; newleaf = new_node(); newleaf->value.data = data; newleaf->key = xmemdupz(str, slen); newleaf->klen = slen; if (addr) newleaf->addr = xmemdupz(addr, alen); else newleaf->addr = NULL; newleaf->alen = alen; newbranch = new_node(); newbranch->value.thres_bit = bitloc; (*parentlink) = newbranch; if (testbit(str, slen, bitloc) ==0) { newbranch->l = newleaf; newbranch->r = node; } else { newbranch->l = node; newbranch->r = newleaf; } } else { if (testbit(str, slen, node->value.thres_bit) != 0) ptree_add_entry_at(str, slen, bitloc, data, addr, alen, &(node->r)); else ptree_add_entry_at(str, slen, bitloc, data, addr, alen, &(node->l)); } } int ptree_add_entry(void *str, size_t sstr, int data, struct sockaddr_storage *addr, size_t alen, struct patricia_node **root) { int *ptr, bitloc, malicious = 0; struct patricia_node *n; if (!(*root)) (*root) = ptree_make_root_node(str, sstr, data, addr, alen); else { ptr = ptree_search_data_nearest_ptr(str, sstr, (*root)); n = container_of(ptr, struct patricia_node, value.data); if (n->klen == sstr && !memcmp(str, n->key, n->klen)) { /* Make sure if entry exists, that we also have the * same data, otherwise, we drop the packet */ if (n->value.data != data) malicious = 1; else if (n->alen != alen) malicious = 1; else if ((n->addr && !addr) || (!n->addr && addr)) malicious = 1; else if (n->alen == alen && n->addr && addr) { if (memcmp(n->addr, addr, alen)) malicious = 1; } return malicious; } bitloc = where_the_bit_differ(str, sstr, n->key, n->klen); ptree_add_entry_at(str, sstr, bitloc, data, addr, alen, root); } return malicious; } static void ptree_remove_entry_r(struct patricia_node *now, struct patricia_node *up, struct patricia_node *up2, char *str, size_t slen, int maxbitplace, struct patricia_node **root) { struct patricia_node *b; if (now->l == NULL && now->r == NULL) { if (now->klen != slen) return; if (memcmp(now->key, str, slen)) return; if (up == NULL) { *root = NULL; free_node(now); return; } b = (up->r == now) ? up->l : up->r; if (up2 == NULL) { *root = b; free_node(now); free_node(up); return; } if (up2->l == up) up2->l = b; else up2->r = b; free_node(now); free_node(up); } else { if (testbit_max(str, now->value.thres_bit, maxbitplace) != 0) ptree_remove_entry_r(now->r, now, up, str, slen, maxbitplace, root); else ptree_remove_entry_r(now->l, now, up, str, slen, maxbitplace, root); } } void ptree_del_entry(void *str, size_t sstr, struct patricia_node **root) { if (!(*root)) return; ptree_remove_entry_r(*root, NULL, NULL, str, sstr, sstr * 8, root); } void ptree_free(struct patricia_node *node) { if (!node) return; if (node->l) ptree_free(node->l); if (node->r) ptree_free(node->r); free_node(node); }