X-Git-Url: https://tinc-vpn.org/git/browse?a=blobdiff_plain;f=src%2Fsubnet.c;h=602177ffef3e146bccb71a49c42eba50112bb572;hb=ac2a1722b71e98010324ed46235e96bfa6e672f5;hp=4fefda6ff8f47996c380eabf2165da7d72b1aa9f;hpb=f6e87ab476a0faf8b124ecaaa27f967d825e6457;p=tinc diff --git a/src/subnet.c b/src/subnet.c index 4fefda6f..602177ff 100644 --- a/src/subnet.c +++ b/src/subnet.c @@ -22,58 +22,126 @@ #include "splay_tree.h" #include "control_common.h" -#include "device.h" #include "hash.h" #include "logger.h" -#include "names.h" #include "net.h" #include "netutl.h" #include "node.h" #include "script.h" #include "subnet.h" -#include "utils.h" #include "xalloc.h" /* lists type of subnet */ - -splay_tree_t *subnet_tree; +uint32_t hash_seed; +splay_tree_t subnet_tree = { + .compare = (splay_compare_t) subnet_compare, + .delete = (splay_action_t) free_subnet, +}; /* Subnet lookup cache */ -static hash_t *ipv4_cache; -static hash_t *ipv6_cache; -static hash_t *mac_cache; +static uint32_t wrapping_add32(uint32_t a, uint32_t b) { + return (uint32_t)((uint64_t)a + b); +} -void subnet_cache_flush(void) { - hash_clear(ipv4_cache); - hash_clear(ipv6_cache); - hash_clear(mac_cache); +static uint32_t wrapping_mul32(uint32_t a, uint32_t b) { + return (uint32_t)((uint64_t)a * b); } -/* Initialising trees */ +static uint32_t hash_function_ipv4_t(const ipv4_t *p) { + /* + This basic hash works because + a) Most IPv4 networks routed via tinc are not /0 + b) Most IPv4 networks have more unique low order bits + */ + uint16_t *halfwidth = (uint16_t *)p; + uint32_t hash = hash_seed; + +#if __BYTE_ORDER == __LITTLE_ENDIAN + // 10.0.x.x/16 part + hash = wrapping_add32(hash, wrapping_mul32(halfwidth[1], 0x9e370001U)); + + // x.x.0.[0-255] part +#if SUBNET_HASH_SIZE >= 0x10000 + return hash ^ halfwidth[0]; +#else + // ensure that we have a /24 with no collisions on 32bit + return hash ^ ntohs(halfwidth[0]); +#endif // _____LP64_____ +#else + // 10.0.x.x/16 part + hash = wrapping_add32(hash, wrapping_mul32(halfwidth[0], 0x9e370001U)); + + // x.x.0.[0-255] part (ntohs is nop on big endian) + return hash ^ halfwidth[1]; +#endif // __BYTE_ORDER == __LITTLE_ENDIAN +} -void init_subnets(void) { - subnet_tree = splay_alloc_tree((splay_compare_t) subnet_compare, (splay_action_t) free_subnet); - ipv4_cache = hash_alloc(0x100, sizeof(ipv4_t)); - ipv6_cache = hash_alloc(0x100, sizeof(ipv6_t)); - mac_cache = hash_alloc(0x100, sizeof(mac_t)); +static uint32_t hash_function_ipv6_t(const ipv6_t *p) { + uint32_t *fullwidth = (uint32_t *)p; + uint32_t hash = hash_seed; + + for(int i = 0; i < 4; i++) { + hash += fullwidth[i]; + hash = wrapping_mul32(hash, 0x9e370001U); + } + + return hash; } -void exit_subnets(void) { - splay_delete_tree(subnet_tree); +static uint32_t hash_function_mac_t(const mac_t *p) { + uint16_t *halfwidth = (uint16_t *)p; + uint32_t hash = hash_seed; + + for(int i = 0; i < 3; i++) { + hash += halfwidth[i]; + hash = wrapping_mul32(hash, 0x9e370001U); + } + + return hash; +} + +hash_define(ipv4_t, SUBNET_HASH_SIZE) +hash_define(ipv6_t, SUBNET_HASH_SIZE) +hash_define(mac_t, SUBNET_HASH_SIZE) + +hash_new(ipv4_t, ipv4_cache); +hash_new(ipv6_t, ipv6_cache); +hash_new(mac_t, mac_cache); + + +void subnet_cache_flush_table(subnet_type_t stype) { + // NOTE: a subnet type of SUBNET_TYPES can be used to clear all hash tables + + if(stype != SUBNET_IPV6) { // ipv4 + hash_clear(ipv4_t, &ipv4_cache); + } + + if(stype != SUBNET_IPV4) { // ipv6 + hash_clear(ipv6_t, &ipv6_cache); + } + + hash_clear(mac_t, &mac_cache); +} + +/* Initialising trees */ - hash_free(ipv4_cache); - hash_free(ipv6_cache); - hash_free(mac_cache); +void init_subnets(void) { + hash_seed = (uint32_t)rand(); + + // tables need to be cleared on startup + subnet_cache_flush_tables(); } -splay_tree_t *new_subnet_tree(void) { - return splay_alloc_tree((splay_compare_t) subnet_compare, NULL); +void exit_subnets(void) { + splay_empty_tree(&subnet_tree); + subnet_cache_flush_tables(); } -void free_subnet_tree(splay_tree_t *subnet_tree) { - splay_delete_tree(subnet_tree); +void init_subnet_tree(splay_tree_t *tree) { + memset(tree, 0, sizeof(*tree)); + tree->compare = (splay_compare_t) subnet_compare; } /* Allocating and freeing space for subnets */ @@ -86,34 +154,67 @@ void free_subnet(subnet_t *subnet) { free(subnet); } +void subnet_cache_flush_tables(void) { + // flushes all the tables + hash_clear(ipv4_t, &ipv4_cache); + hash_clear(ipv6_t, &ipv6_cache); + hash_clear(mac_t, &mac_cache); +} + +void subnet_cache_flush(subnet_t *subnet) { + switch(subnet->type) { + case SUBNET_IPV4: + if(subnet->net.ipv4.prefixlength == 32) { + hash_delete(ipv4_t, &ipv4_cache, &subnet->net.ipv4.address); + return; + } + + break; + + case SUBNET_IPV6: + if(subnet->net.ipv4.prefixlength == 128) { + hash_delete(ipv6_t, &ipv6_cache, &subnet->net.ipv6.address); + return; + } + + break; + + case SUBNET_MAC: + hash_delete(mac_t, &mac_cache, &subnet->net.mac.address); + return; + } + + subnet_cache_flush_table(subnet->type); +} + /* Adding and removing subnets */ void subnet_add(node_t *n, subnet_t *subnet) { subnet->owner = n; - splay_insert(subnet_tree, subnet); + splay_insert(&subnet_tree, subnet); if(n) { - splay_insert(n->subnet_tree, subnet); + splay_insert(&n->subnet_tree, subnet); } - subnet_cache_flush(); + subnet_cache_flush(subnet); } void subnet_del(node_t *n, subnet_t *subnet) { if(n) { - splay_delete(n->subnet_tree, subnet); + splay_delete(&n->subnet_tree, subnet); } - splay_delete(subnet_tree, subnet); + splay_delete(&subnet_tree, subnet); - subnet_cache_flush(); + subnet_cache_flush(subnet); } /* Subnet lookup routines */ -subnet_t *lookup_subnet(const node_t *owner, const subnet_t *subnet) { - return splay_search(owner->subnet_tree, subnet); +subnet_t *lookup_subnet(node_t *owner, const subnet_t *subnet) { + return splay_search(&owner->subnet_tree, subnet); } subnet_t *lookup_subnet_mac(const node_t *owner, const mac_t *address) { @@ -121,13 +222,13 @@ subnet_t *lookup_subnet_mac(const node_t *owner, const mac_t *address) { // Check if this address is cached - if((r = hash_search(mac_cache, address))) { + if((r = hash_search(mac_t, &mac_cache, address))) { return r; } // Search all subnets for a matching one - for splay_each(subnet_t, p, owner ? owner->subnet_tree : subnet_tree) { + for splay_each(subnet_t, p, owner ? &owner->subnet_tree : &subnet_tree) { if(!p || p->type != SUBNET_MAC) { continue; } @@ -144,7 +245,7 @@ subnet_t *lookup_subnet_mac(const node_t *owner, const mac_t *address) { // Cache the result if(r) { - hash_insert(mac_cache, address, r); + hash_insert(mac_t, &mac_cache, address, r); } return r; @@ -155,13 +256,13 @@ subnet_t *lookup_subnet_ipv4(const ipv4_t *address) { // Check if this address is cached - if((r = hash_search(ipv4_cache, address))) { + if((r = hash_search(ipv4_t, &ipv4_cache, address))) { return r; } // Search all subnets for a matching one - for splay_each(subnet_t, p, subnet_tree) { + for splay_each(subnet_t, p, &subnet_tree) { if(!p || p->type != SUBNET_IPV4) { continue; } @@ -178,7 +279,7 @@ subnet_t *lookup_subnet_ipv4(const ipv4_t *address) { // Cache the result if(r) { - hash_insert(ipv4_cache, address, r); + hash_insert(ipv4_t, &ipv4_cache, address, r); } return r; @@ -189,13 +290,13 @@ subnet_t *lookup_subnet_ipv6(const ipv6_t *address) { // Check if this address is cached - if((r = hash_search(ipv6_cache, address))) { + if((r = hash_search(ipv6_t, &ipv6_cache, address))) { return r; } // Search all subnets for a matching one - for splay_each(subnet_t, p, subnet_tree) { + for splay_each(subnet_t, p, &subnet_tree) { if(!p || p->type != SUBNET_IPV6) { continue; } @@ -212,7 +313,7 @@ subnet_t *lookup_subnet_ipv6(const ipv6_t *address) { // Cache the result if(r) { - hash_insert(ipv6_cache, address, r); + hash_insert(ipv6_t, &ipv6_cache, address, r); } return r; @@ -243,7 +344,7 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) { name = up ? "subnet-up" : "subnet-down"; if(!subnet) { - for splay_each(subnet_t, subnet, owner->subnet_tree) { + for splay_each(subnet_t, subnet, &owner->subnet_tree) { if(!net2str(netstr, sizeof(netstr), subnet)) { continue; } @@ -286,7 +387,7 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) { } bool dump_subnets(connection_t *c) { - for splay_each(subnet_t, subnet, subnet_tree) { + for splay_each(subnet_t, subnet, &subnet_tree) { char netstr[MAXNETSTR]; if(!net2str(netstr, sizeof(netstr), subnet)) {