+splay_tree_t *subnet_tree;
+
+/* Subnet lookup cache */
+
+static ipv4_t cache_ipv4_address[2];
+static subnet_t *cache_ipv4_subnet[2];
+static bool cache_ipv4_valid[2];
+static int cache_ipv4_slot;
+
+static ipv6_t cache_ipv6_address[2];
+static subnet_t *cache_ipv6_subnet[2];
+static bool cache_ipv6_valid[2];
+static int cache_ipv6_slot;
+
+static mac_t cache_mac_address[2];
+static subnet_t *cache_mac_subnet[2];
+static bool cache_mac_valid[2];
+static int cache_mac_slot;
+
+void subnet_cache_flush(void) {
+ cache_ipv4_valid[0] = cache_ipv4_valid[1] = false;
+ cache_ipv6_valid[0] = cache_ipv6_valid[1] = false;
+ cache_mac_valid[0] = cache_mac_valid[1] = false;
+}
+
+/* Subnet comparison */
+
+static int subnet_compare_mac(const subnet_t *a, const subnet_t *b) {
+ int result;
+
+ result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof a->net.mac.address);
+
+ if(result)
+ return result;
+
+ result = a->weight - b->weight;
+
+ if(result || !a->owner || !b->owner)
+ return result;
+
+ return strcmp(a->owner->name, b->owner->name);
+}
+
+static int subnet_compare_ipv4(const subnet_t *a, const subnet_t *b) {
+ int result;
+
+ result = b->net.ipv4.prefixlength - a->net.ipv4.prefixlength;
+
+ if(result)
+ return result;
+
+ result = memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t));
+
+ if(result)
+ return result;
+
+ result = a->weight - b->weight;
+
+ if(result || !a->owner || !b->owner)
+ return result;
+
+ return strcmp(a->owner->name, b->owner->name);
+}
+
+static int subnet_compare_ipv6(const subnet_t *a, const subnet_t *b) {
+ int result;
+
+ result = b->net.ipv6.prefixlength - a->net.ipv6.prefixlength;
+
+ if(result)
+ return result;
+
+ result = memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t));
+
+ if(result)
+ return result;
+
+ result = a->weight - b->weight;
+
+ if(result || !a->owner || !b->owner)
+ return result;
+
+ return strcmp(a->owner->name, b->owner->name);
+}
+
+int subnet_compare(const subnet_t *a, const subnet_t *b) {
+ int result;
+
+ result = a->type - b->type;
+
+ if(result)
+ return result;
+
+ switch (a->type) {
+ case SUBNET_MAC:
+ return subnet_compare_mac(a, b);
+ case SUBNET_IPV4:
+ return subnet_compare_ipv4(a, b);
+ case SUBNET_IPV6:
+ return subnet_compare_ipv6(a, b);
+ default:
+ logger(LOG_ERR, "subnet_compare() was called with unknown subnet type %d, exitting!",
+ a->type);
+ exit(0);
+ }
+
+ return 0;
+}
+
+/* Initialising trees */
+
+void init_subnets(void) {
+ subnet_tree = splay_alloc_tree((splay_compare_t) subnet_compare, (splay_action_t) free_subnet);
+
+ subnet_cache_flush();
+}
+
+void exit_subnets(void) {
+ splay_delete_tree(subnet_tree);
+}
+
+splay_tree_t *new_subnet_tree(void) {
+ return splay_alloc_tree((splay_compare_t) subnet_compare, NULL);
+}
+
+void free_subnet_tree(splay_tree_t *subnet_tree) {
+ splay_delete_tree(subnet_tree);
+}