#include "radix.h" #define GET_BIT(key, mask) ( ((key[0] & mask[0]) | (key[1] & mask[1])) != 0 ) #define SHIFT_MASK(mask) mask[0] >>= 1, mask[1] = (mask[1] >> 1) | (!mask[0] * (1ul << 63)) int radix_insert(radix_holder_t, record_t); int radix_search(radix_holder_t, record_t); radix_holder_t create_holder() { radix_holder_t ret = calloc(1, sizeof(struct radix_holder)); return ret; } static radix_node_t create_node(record_t record) { radix_node_t ret = malloc(sizeof(struct radix_node)); memcpy(&ret->_data, record, sizeof(struct routing_record)); ret->_l = ret->_r = NULL; return ret; } static radix_node_t clone_node(radix_node_t n) { radix_node_t ret = malloc(sizeof(struct radix_node)); memcpy(ret, n, sizeof(struct radix_node)); return ret; } static void split_node(radix_node_t n, unsigned bit) { radix_node_t tmp = clone_node(n); n->_data._mask = bit; n->_l = n->_r = tmp; n->_data._rule = INVALID_RULE; } int destory_holder(struct radix_holder* t) { destory_node(t->_root); free(t); return 1; } static int destory_node(struct radix_node* n) { int ret = 0; if (!n) return ret; ret = destory_node(n->_l) + destory_node(n->_r); free(n); return ret; } int radix_insert(radix_holder_t t, record_t /*restrict*/ r) { unsigned char b; unsigned char ibit = 0; unsigned long mask[2] = {0x01ul << 63, 0x00}; radix_node_t node; radix_node_t* next; if (!r || !t) return 0; node = t->_root; next = &t->_root; if (!node) { t->_root = create_node(r); return t->_size = 1; } /*todo zkontrolovat mezni hodnoty, masku 128 a tak.. */ for (ibit = 0; ibit < r->_mask; ++ibit, SHIFT_MASK(mask)) { b = GET_BIT(r->_ip, mask); if (ibit == node->_data._mask) { /* node ends */ next = b ? &node->_l : &node->_r; if (!*next) { /* add child */ *next = create_node(r); ++t->_size; return 1; } node = *next; continue; } if (b != GET_BIT(node->_data._ip, mask)) {/* bit differs -> split node */ split_node(node, ibit); next = b ? &node->_l : &node->_r; /* new node */ *next = create_node(r); ++t->_size; return 1; } } /* insert before */ if (node->_data._mask > r->_mask) { split_node(node, ibit); b = GET_BIT(node->_data._ip, mask); next = b ? &node->_r : &node->_l; *next = NULL; memcpy(&node->_data, r, sizeof(struct routing_record)); ++t->_size; return 1; } /* exact match */ if (node->_data._mask == r->_mask && ibit == r->_mask) { if (node->_data._rule != INVALID_RULE) return 0; /* make valid */ node->_data._rule = r->_rule; ++t->_size; return 1; } printf("!!!!!!!!!!!!!!\n"); return 0; } int radix_search(radix_holder_t t, record_t r) { radix_node_t iter; int ibit; unsigned char b; int m; unsigned long mask[2] = {0x01ul << 63, 0x00}; if (!r) return INVALID_RULE; if (!t || !t->_root) return r->_rule = INVALID_RULE; iter = t->_root; for (ibit = 0; ibit < r->_mask; ++ibit, SHIFT_MASK(mask)) { b = GET_BIT(r->_ip, mask); if (ibit < iter->_data._mask && /* bit match */ b == GET_BIT(iter->_data._ip, mask)) continue; if (ibit < iter->_data._mask) { /* bit mismatch */ return r->_rule; /* = ibit == iter->_data._mask ? iter->_data._rule : r->_rule; */ } /* mask ends with full match */ if (iter->_data._rule != INVALID_RULE) { r->_rule = iter->_data._rule; m = iter->_data._mask; } iter = b ? iter->_l : iter->_r; if (!iter) return r->_rule; } if (ibit == iter->_data._mask && iter->_data._rule != INVALID_RULE) { r->_rule = iter->_data._rule; m = iter->_data._mask; } r->_mask = m; return r->_rule; } /*--------------------------------------------------------------------------------*/ /* TESTING */ /*--------------------------------------------------------------------------------*/ #include #include #include #define RULES_FILE "../data/routing-data" #define TEST_FILE "../data/test-data" #define TEST_ROWS 39222 /*39222*/ #define READ_MODE "r" int parse_line(FILE* f, record_t r) { int ikey, jkey, mask, rule; unsigned long key[2] = {0,0}; struct in6_addr addr; char ipv6_cidr[50]; char *separator; if (fgets(ipv6_cidr, sizeof(ipv6_cidr), f) == NULL) { if (!feof(f)) printf("error reading\n"); return 0; } ipv6_cidr[strcspn(ipv6_cidr, "\n")] = 0; separator = strchr(ipv6_cidr, '/'); if (!separator) { printf("invalid CIDR notation\n"); return 0; } *separator++ = '\0'; mask = atoi(separator); separator = strchr(separator, ' '); rule = atoi(++separator); if (mask < 0 || mask > 128) { printf("invalid prefix length\n"); return 0; } r->_mask = mask; if (rule < 0 || rule > 65536) { printf("invalid pop\n"); return 0; } r->_rule = rule; if (inet_pton(AF_INET6, ipv6_cidr, &addr) != 1) { printf("%s invalid address\n", ipv6_cidr); return 0; } for (ikey = 0; ikey < 2; ++ikey) { for (jkey = 0; jkey < 8; ++jkey) { key[ikey] = (key[ikey] << 8) | addr.__in6_u.__u6_addr8[jkey + (ikey * 8)]; } } r->_ip[0] = key[0]; r->_ip[1] = key[1]; return 1; } int load_input(radix_holder_t t) { struct routing_record r; FILE* f; int cnt = 0; if (!(f = fopen(RULES_FILE, READ_MODE))) { puts("cant open input file"); return 0; } while (parse_line(f, &r)) { if (radix_insert(t, &r)) ++cnt; else printf("WARNING: conflicting insert\n"); } fclose(f); return cnt; } int load_tests(struct routing_record** ref) { FILE* f; int cnt = 0; struct routing_record* r = malloc(sizeof(struct routing_record) * TEST_ROWS); if (!(f = fopen(TEST_FILE, READ_MODE))) { puts("cant open test file"); return 0; } for (cnt = 0; cnt < TEST_ROWS; ++cnt) { if (!parse_line(f, &r[cnt])) { printf("WARNING test parse failed %d\n", cnt); break; } } fclose(f); *ref = r; return cnt; } int fire_tests(radix_holder_t t, struct routing_record* r) { int row; int wrong = TEST_ROWS; unsigned short ref_pop, ref_scope; clock_t start, end; double lookup_time; start = clock(); for (row = 0; row < TEST_ROWS; ++row) { ref_pop = r[row]._rule; ref_scope = r[row]._mask; r[row]._rule = INVALID_RULE; radix_search(t, &r[row]); if (ref_pop == r[row]._rule && ref_scope == r[row]._mask) { --wrong; } else { if (ref_pop != r[row]._rule) printf("pop: %d got: %d @ %d\n", ref_pop, r[row]._rule, row ); if (ref_scope != r[row]._mask) printf("scp: %d got: %d @ %d\n", ref_scope, r[row]._mask, row ); } } end = clock(); lookup_time = ((double) (end - start)) / CLOCKS_PER_SEC * 1000; printf("%d lookups in %f ms (%f microseconds per op)\n", TEST_ROWS, lookup_time, ((end - start)/(double)TEST_ROWS)); free(r); return wrong; return 0; } int main() { struct routing_record* ref; radix_holder_t t = create_holder(); printf("Loaded %d rules\n", load_input(t) ); printf("Loaded %d tests\n", load_tests(&ref) ); printf("%d tests wrong\n", fire_tests(t, ref) ); destory_holder(t); return 0; }