Optimize IpToPeerMap with a trie
This commit is contained in:
parent
1328d98ad2
commit
7b7fb6126b
3 changed files with 718 additions and 37 deletions
|
@ -4,6 +4,7 @@
|
||||||
#include "ip_to_peer_map.h"
|
#include "ip_to_peer_map.h"
|
||||||
#include "bit_ops.h"
|
#include "bit_ops.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
IpToPeerMap::IpToPeerMap() {
|
IpToPeerMap::IpToPeerMap() {
|
||||||
|
|
||||||
|
@ -12,10 +13,8 @@ IpToPeerMap::IpToPeerMap() {
|
||||||
IpToPeerMap::~IpToPeerMap() {
|
IpToPeerMap::~IpToPeerMap() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IpToPeerMap::InsertV4(const void *addr, int cidr, void *peer) {
|
bool IpToPeerMap::InsertV4(uint32 ip, int cidr, void *peer) {
|
||||||
uint32 mask = cidr == 32 ? 0xffffffff : ~(0xffffffff >> cidr);
|
ipv4_.Insert(ip, cidr, peer);
|
||||||
Entry4 e = {ReadBE32(addr) & mask, mask, peer};
|
|
||||||
ipv4_.push_back(e);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,23 +28,11 @@ bool IpToPeerMap::InsertV6(const void *addr, int cidr, void *peer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void *IpToPeerMap::LookupV4(uint32 ip) {
|
void *IpToPeerMap::LookupV4(uint32 ip) {
|
||||||
uint32 best_mask = 0;
|
return ipv4_.Lookup(ip);
|
||||||
void *best_peer = NULL;
|
|
||||||
for (auto it = ipv4_.begin(); it != ipv4_.end(); ++it) {
|
|
||||||
if (it->ip == (ip & it->mask) && it->mask >= best_mask) {
|
|
||||||
best_mask = it->mask;
|
|
||||||
best_peer = it->peer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return best_peer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *IpToPeerMap::LookupV4DefaultPeer() {
|
void *IpToPeerMap::LookupV4DefaultPeer() {
|
||||||
for (auto it = ipv4_.begin(); it != ipv4_.end(); ++it) {
|
return ipv4_.LookupExact(0, 0);
|
||||||
if (it->mask == 0)
|
|
||||||
return it->peer;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *IpToPeerMap::LookupV6DefaultPeer() {
|
void *IpToPeerMap::LookupV6DefaultPeer() {
|
||||||
|
@ -76,15 +63,8 @@ void *IpToPeerMap::LookupV6(const void *addr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void IpToPeerMap::RemovePeer(void *peer) {
|
void IpToPeerMap::RemovePeer(void *peer) {
|
||||||
{
|
assert(0);
|
||||||
size_t n = ipv4_.size();
|
// todo: remove peer also from ipv4_
|
||||||
Entry4 *r = &ipv4_[0], *w = r;
|
|
||||||
for (size_t i = 0; i != n; i++, r++) {
|
|
||||||
if (r->peer != peer)
|
|
||||||
*w++ = *r;
|
|
||||||
}
|
|
||||||
ipv4_.resize(w - &ipv4_[0]);
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
size_t n = ipv6_.size();
|
size_t n = ipv6_.size();
|
||||||
Entry6 *r = &ipv6_[0], *w = r;
|
Entry6 *r = &ipv6_[0], *w = r;
|
||||||
|
@ -95,3 +75,678 @@ void IpToPeerMap::RemovePeer(void *peer) {
|
||||||
ipv6_.resize(w - &ipv6_[0]);
|
ipv6_.resize(w - &ipv6_[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma warning (disable: 4200) // warning C4200: nonstandard extension used: zero-sized array in struct/union
|
||||||
|
struct RoutingTrie32::Node {
|
||||||
|
uint32 key;
|
||||||
|
// bits == 0 if this is a leaf
|
||||||
|
uint8 pos, bits;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
Node *parent;
|
||||||
|
uint32 full_children, empty_children;
|
||||||
|
Node *child[0];
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
Node *leaf_next;
|
||||||
|
Value leaf_value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static inline uint32 prefix_mismatch(uint32 key, RoutingTrie32::Node *n) {
|
||||||
|
uint32 prefix = n->key;
|
||||||
|
return (key ^ prefix) & (prefix | (uint32)-(int32)prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32 make_cidr_mask(uint8 cidr) {
|
||||||
|
return cidr == 0 ? 0 : 0xffffffff << (32 - cidr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IS_LEAF(n) (n->bits == 0)
|
||||||
|
#define GET_INDEX(k, n) ((n->key ^ k) >> n->pos)
|
||||||
|
|
||||||
|
#define NODE_IS_OLEAF(n) ((intptr_t)(n) & 1)
|
||||||
|
#define NODE_IS_NULL_OR_OLEAF(n) ((n) == 0 || NODE_IS_OLEAF(n))
|
||||||
|
#define VALUE_TO_OLEAF(n) ((Node*)((intptr_t)(n) + 1))
|
||||||
|
#define VALUE_FROM_OLEAF(n) ((void*)((intptr_t)(n) - 1))
|
||||||
|
|
||||||
|
static RoutingTrie32::Node *NewNode(uint32 key, int pos, int bits) {
|
||||||
|
RoutingTrie32::Node *n = (RoutingTrie32::Node *)malloc(offsetof(RoutingTrie32::Node, child[(uint32)(1U << bits)]));
|
||||||
|
if (n) {
|
||||||
|
n->parent = NULL;
|
||||||
|
n->pos = pos;
|
||||||
|
n->bits = bits;
|
||||||
|
n->full_children = 0;
|
||||||
|
n->empty_children = 1U << bits;
|
||||||
|
uint32 s = pos + bits;
|
||||||
|
n->key = (s < 32) ? key >> s << s : 0;
|
||||||
|
memset(n->child, 0, n->empty_children * sizeof(RoutingTrie32::Node*));
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RoutingTrie32::Node *NewLeaf(uint32 key, uint8 leaf_pos, RoutingTrie32::Value value) {
|
||||||
|
RoutingTrie32::Node *n = (RoutingTrie32::Node *)malloc(sizeof(RoutingTrie32::Node));
|
||||||
|
if (n) {
|
||||||
|
n->key = key;
|
||||||
|
n->bits = 0;
|
||||||
|
n->pos = leaf_pos;
|
||||||
|
n->leaf_value = value;
|
||||||
|
n->leaf_next = NULL;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FreeNode(RoutingTrie32::Node *n) {
|
||||||
|
free(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RecursiveFreeNode(RoutingTrie32::Node *n) {
|
||||||
|
RoutingTrie32::Node *cn;
|
||||||
|
|
||||||
|
if (n->bits == 0) {
|
||||||
|
while ((cn = n->leaf_next) != NULL) {
|
||||||
|
n->leaf_next = cn->leaf_next;
|
||||||
|
FreeNode(cn);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint32 items = 1 << n->bits;
|
||||||
|
for (uint32 i = 0; i != items; i++) {
|
||||||
|
RoutingTrie32::Node *cn = n->child[i];
|
||||||
|
if (!NODE_IS_NULL_OR_OLEAF(cn))
|
||||||
|
RecursiveFreeNode(cn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FreeNode(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RoutingTrie32::RoutingTrie32()
|
||||||
|
: root_(NULL) {
|
||||||
|
}
|
||||||
|
|
||||||
|
RoutingTrie32::~RoutingTrie32() {
|
||||||
|
if (root_)
|
||||||
|
RecursiveFreeNode(root_);
|
||||||
|
}
|
||||||
|
|
||||||
|
RoutingTrie32::Value RoutingTrie32::Lookup(uint32 ip) {
|
||||||
|
uint32 key = ip;
|
||||||
|
Node *n = root_, *pn = n, *ppn;
|
||||||
|
int cindex = 0;
|
||||||
|
if (!n)
|
||||||
|
return NULL;
|
||||||
|
// Find the longest prefix match
|
||||||
|
for (;;) {
|
||||||
|
uint32 index = GET_INDEX(key, n);
|
||||||
|
if (index >> n->bits)
|
||||||
|
break; // mismatch in skipped bits
|
||||||
|
if (IS_LEAF(n))
|
||||||
|
return n->leaf_value;
|
||||||
|
pn = n;
|
||||||
|
cindex = index;
|
||||||
|
n = n->child[index];
|
||||||
|
if (NODE_IS_NULL_OR_OLEAF(n)) {
|
||||||
|
if (!n)
|
||||||
|
goto backtrace;
|
||||||
|
// node is an optimized leaf
|
||||||
|
return VALUE_FROM_OLEAF(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// backtrace for longest prefix
|
||||||
|
for (;;) {
|
||||||
|
if (prefix_mismatch(key, n))
|
||||||
|
goto backtrace;
|
||||||
|
if (IS_LEAF(n)) {
|
||||||
|
for (;;) {
|
||||||
|
if (((n->key ^ key) >> n->pos) == 0)
|
||||||
|
return n->leaf_value;
|
||||||
|
if (n->leaf_next == NULL) {
|
||||||
|
if (n->pos == 32)
|
||||||
|
return n->leaf_value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
n = n->leaf_next;
|
||||||
|
}
|
||||||
|
goto backtrace;
|
||||||
|
}
|
||||||
|
|
||||||
|
ppn = n;
|
||||||
|
n = n->child[0];
|
||||||
|
if (NODE_IS_NULL_OR_OLEAF(n)) {
|
||||||
|
if (n) {
|
||||||
|
if (((ppn->key ^ key) >> ppn->pos) == 0)
|
||||||
|
return VALUE_FROM_OLEAF(n);
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
backtrace:
|
||||||
|
// step up to previous parent when we used all bits in current
|
||||||
|
while (cindex == 0) {
|
||||||
|
uint32 pkey = pn->key;
|
||||||
|
pn = pn->parent;
|
||||||
|
if (!pn)
|
||||||
|
return 0;
|
||||||
|
cindex = (pn->key ^ pkey) >> pn->pos;
|
||||||
|
}
|
||||||
|
// strip lsb of cindex and find child
|
||||||
|
cindex &= cindex - 1;
|
||||||
|
assert(cindex < (1 << pn->bits));
|
||||||
|
n = pn->child[cindex];
|
||||||
|
if (!NODE_IS_NULL_OR_OLEAF(n))
|
||||||
|
break;
|
||||||
|
if (n) {
|
||||||
|
uint32 nkey = pn->key + (cindex << pn->pos);
|
||||||
|
if (((nkey ^ key) >> pn->pos) == 0)
|
||||||
|
return VALUE_FROM_OLEAF(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RoutingTrie32::InsertLeafInto(Node **nn, uint8 leaf_pos, Value value) {
|
||||||
|
// put higher cidr higher up
|
||||||
|
Node *n = *nn;
|
||||||
|
assert(IS_LEAF(n));
|
||||||
|
uint32 key = n->key;
|
||||||
|
do {
|
||||||
|
if (leaf_pos < n->pos)
|
||||||
|
break;
|
||||||
|
if (leaf_pos == n->pos) {
|
||||||
|
n->leaf_value = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
nn = &n->leaf_next;
|
||||||
|
} while ((n = *nn) != NULL);
|
||||||
|
Node *leaf = NewLeaf(key, leaf_pos, value);
|
||||||
|
if (leaf == NULL)
|
||||||
|
return false;
|
||||||
|
leaf->leaf_next = *nn;
|
||||||
|
*nn = leaf;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool IsFull(RoutingTrie32::Node *pn, RoutingTrie32::Node *n) {
|
||||||
|
return !NODE_IS_NULL_OR_OLEAF(n) && (n->pos + n->bits) == pn->pos && !IS_LEAF(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoutingTrie32::PutChild(Node *pn, uint32 i, Node *n) {
|
||||||
|
Node *on = pn->child[i];
|
||||||
|
pn->child[i] = n;
|
||||||
|
|
||||||
|
pn->empty_children += (n == NULL) - (on == NULL);
|
||||||
|
pn->full_children += IsFull(pn, n) - IsFull(pn, on);
|
||||||
|
|
||||||
|
assert(pn->empty_children < 0x80000000);
|
||||||
|
assert(pn->full_children < 0x80000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RoutingTrie32::Insert(uint32 ip, int cidr, Value value) {
|
||||||
|
uint32 key = ip;
|
||||||
|
Node **nn = &root_, *n = root_, *pn = NULL, *leaf, *tn = NULL, *leaf_to_free = NULL;
|
||||||
|
uint8 leaf_pos = 32 - cidr;
|
||||||
|
|
||||||
|
if (n == NULL) {
|
||||||
|
root_ = NewLeaf(key, leaf_pos, value);
|
||||||
|
return (root_ != NULL);
|
||||||
|
}
|
||||||
|
assert(!NODE_IS_OLEAF(n));
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
uint32 index = GET_INDEX(key, n);
|
||||||
|
if (index >> n->bits) {
|
||||||
|
force_add:
|
||||||
|
// n is a node and the key doesn't match, allocate a new node
|
||||||
|
// with two elements and insert it.
|
||||||
|
if (!(tn = NewNode(key, FindLastSetBit32(key ^ n->key), 1)))
|
||||||
|
return false;
|
||||||
|
tn->parent = pn;
|
||||||
|
// can convert leaf node to oleaf?
|
||||||
|
if (IS_LEAF(n)) {
|
||||||
|
if (tn->pos == n->pos && n->leaf_next == NULL) {
|
||||||
|
leaf_to_free = n;
|
||||||
|
n = VALUE_TO_OLEAF(n->leaf_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PutChild(tn, GET_INDEX(key, tn) ^ 1, n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (IS_LEAF(n)) {
|
||||||
|
if (key != n->key)
|
||||||
|
goto force_add;
|
||||||
|
return InsertLeafInto(nn, leaf_pos, value);
|
||||||
|
}
|
||||||
|
pn = n;
|
||||||
|
nn = &n->child[index];
|
||||||
|
if ((n = *nn) == NULL) {
|
||||||
|
tn = pn;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (NODE_IS_OLEAF(n)) {
|
||||||
|
if (!(n = NewLeaf(pn->key + (index << pn->pos), pn->pos, VALUE_FROM_OLEAF(n))))
|
||||||
|
return false;
|
||||||
|
*nn = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create either leaf or oleaf
|
||||||
|
if (tn->pos == leaf_pos) {
|
||||||
|
leaf = VALUE_TO_OLEAF(value);
|
||||||
|
} else if (!(leaf = NewLeaf(key, leaf_pos, value))) {
|
||||||
|
if (tn != pn)
|
||||||
|
FreeNode(tn);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Start making irreversible changes here
|
||||||
|
if (leaf_to_free)
|
||||||
|
FreeNode(leaf_to_free);
|
||||||
|
|
||||||
|
if (tn != pn) {
|
||||||
|
if (!NODE_IS_OLEAF(n) && !IS_LEAF(n))
|
||||||
|
n->parent = tn;
|
||||||
|
|
||||||
|
if (pn) {
|
||||||
|
PutChild(pn, GET_INDEX(key, pn), tn);
|
||||||
|
} else {
|
||||||
|
root_ = tn;
|
||||||
|
}
|
||||||
|
pn = tn;
|
||||||
|
}
|
||||||
|
|
||||||
|
PutChild(pn, GET_INDEX(key, pn), leaf);
|
||||||
|
|
||||||
|
Rebalance(pn);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RoutingTrie32::Delete(uint32 ip, int cidr) {
|
||||||
|
uint32 key = ip;
|
||||||
|
|
||||||
|
Node *n = root_, *pn = NULL;
|
||||||
|
uint32 pn_index = 0;
|
||||||
|
|
||||||
|
if (n == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint8 leaf_pos = 32 - cidr;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
uint32 index = GET_INDEX(key, n);
|
||||||
|
if (index >> n->bits)
|
||||||
|
return false;
|
||||||
|
if (IS_LEAF(n)) {
|
||||||
|
if (n->key != key)
|
||||||
|
return false;
|
||||||
|
if (n->pos == leaf_pos) {
|
||||||
|
if (pn == NULL) {
|
||||||
|
root_ = n->leaf_next;
|
||||||
|
} else {
|
||||||
|
PutChild(pn, pn_index, n->leaf_next);
|
||||||
|
if (n->leaf_next == NULL)
|
||||||
|
Rebalance(pn);
|
||||||
|
}
|
||||||
|
FreeNode(n);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Node **nn = &n->leaf_next;
|
||||||
|
while (*nn) {
|
||||||
|
if ((*nn)->pos == leaf_pos) {
|
||||||
|
*nn = (*nn)->leaf_next;
|
||||||
|
FreeNode(*nn);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
nn = &(*nn)->leaf_next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pn = n;
|
||||||
|
pn_index = index;
|
||||||
|
n = n->child[index];
|
||||||
|
if (NODE_IS_NULL_OR_OLEAF(n)) {
|
||||||
|
if (n && key == pn->key + (index << pn->pos) && pn->pos == leaf_pos) {
|
||||||
|
PutChild(pn, index, NULL);
|
||||||
|
Rebalance(pn);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RoutingTrie32::Value RoutingTrie32::LookupExact(uint32 ip, int cidr) {
|
||||||
|
uint32 key = ip;
|
||||||
|
Node *n = root_, *pn;
|
||||||
|
if (n == NULL)
|
||||||
|
return NULL;
|
||||||
|
uint8 leaf_pos = 32 - cidr;
|
||||||
|
for (;;) {
|
||||||
|
uint32 index = GET_INDEX(key, n);
|
||||||
|
if (index >> n->bits)
|
||||||
|
return NULL;
|
||||||
|
if (IS_LEAF(n)) {
|
||||||
|
if (n->key != key)
|
||||||
|
return NULL;
|
||||||
|
do {
|
||||||
|
if (n->pos == leaf_pos)
|
||||||
|
return n->leaf_value;
|
||||||
|
n = n->leaf_next;
|
||||||
|
} while (n);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pn = n;
|
||||||
|
n = n->child[index];
|
||||||
|
if (NODE_IS_NULL_OR_OLEAF(n))
|
||||||
|
return (n && key == pn->key + (index << pn->pos) && pn->pos == leaf_pos) ? n->leaf_value : NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoutingTrie32::Rebalance(Node *n) {
|
||||||
|
// Always resize |n| and its parent. For each parent where
|
||||||
|
// Resize returns true resize also its parent.
|
||||||
|
Node *np = n->parent;
|
||||||
|
Resize(n);
|
||||||
|
while (np) {
|
||||||
|
n = np;
|
||||||
|
np = n->parent;
|
||||||
|
if (!Resize(n))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoutingTrie32::ResizeChildren(Node *pn) {
|
||||||
|
for (uint32 i = 0, i_end = 1U << pn->bits; i != i_end; i++) {
|
||||||
|
Node *n = pn->child[i];
|
||||||
|
if (IsFull(pn, n))
|
||||||
|
Resize(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kHalveThreshold = 25,
|
||||||
|
kInflateThreshold = 50,
|
||||||
|
kHalveThresholdRoot = 15,
|
||||||
|
kInflateThresholdRoot = 30,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool RoutingTrie32::Resize(Node *n) {
|
||||||
|
assert(!IS_LEAF(n));
|
||||||
|
|
||||||
|
Node **pn = n->parent ? &n->parent->child[GET_INDEX(n->key, n->parent)] : &root_;
|
||||||
|
bool did_work = false;
|
||||||
|
|
||||||
|
if (n->empty_children >= (1U << n->bits) - 1) {
|
||||||
|
Collapse(pn);
|
||||||
|
n = *pn;
|
||||||
|
if (n == NULL || IS_LEAF(n))
|
||||||
|
return true;
|
||||||
|
did_work = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool did_inflate = false;
|
||||||
|
|
||||||
|
// Double as long as the resulting node has a number of
|
||||||
|
// nonempty nodes that are above the threshold.
|
||||||
|
while ((n->full_children > 0 && 50 * (n->full_children + (1U << n->bits) - n->empty_children) >=
|
||||||
|
(n->parent && !(n->pos == 9) ? kInflateThreshold : kInflateThresholdRoot) * (1U << n->bits)) && n->bits < 16) {
|
||||||
|
if (!Inflate(pn))
|
||||||
|
break;
|
||||||
|
n = *pn;
|
||||||
|
did_work = true;
|
||||||
|
did_inflate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Halve as long as the number of empty children in this
|
||||||
|
// node is above threshold.
|
||||||
|
while (n->bits > 1 && 100 * ((1U << n->bits) - n->empty_children) <
|
||||||
|
(n->parent && !(n->pos == 8) ? kHalveThreshold : kHalveThresholdRoot) * (1U << n->bits)) {
|
||||||
|
assert(!did_inflate);
|
||||||
|
if (!Halve(pn))
|
||||||
|
break;
|
||||||
|
|
||||||
|
n = *pn;
|
||||||
|
did_work = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->empty_children >= (1U << n->bits) - 1) {
|
||||||
|
Collapse(pn);
|
||||||
|
n = *pn;
|
||||||
|
if (n == NULL || IS_LEAF(n))
|
||||||
|
return true;
|
||||||
|
did_work = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (did_work)
|
||||||
|
ResizeChildren(n);
|
||||||
|
|
||||||
|
return did_work;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoutingTrie32::UpdateParent(Node *pn) {
|
||||||
|
uint32 i_end = 1U << pn->bits;
|
||||||
|
uint32 mask_of_halves = 0;
|
||||||
|
|
||||||
|
for (uint32 i = 0; i != i_end; i++) {
|
||||||
|
Node *n = pn->child[i];
|
||||||
|
if (n == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
mask_of_halves |= (i & 1) + 1;
|
||||||
|
|
||||||
|
if (NODE_IS_NULL_OR_OLEAF(n) || IS_LEAF(n))
|
||||||
|
continue;
|
||||||
|
Node *op = n->parent;
|
||||||
|
n->parent = pn;
|
||||||
|
if (op == NULL)
|
||||||
|
UpdateParent(n);
|
||||||
|
}
|
||||||
|
// Collapse right away if there's too many children
|
||||||
|
if (pn->empty_children + 1 >= i_end) {
|
||||||
|
Collapse(pn->parent ? &pn->parent->child[GET_INDEX(pn->key, pn->parent)] : &root_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask_of_halves != 3) {
|
||||||
|
// Only one half of the entries are actually used. Perform a halving operation.
|
||||||
|
Halve(pn->parent ? &pn->parent->child[GET_INDEX(pn->key, pn->parent)] : &root_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RoutingTrie32::Node *RoutingTrie32::ConvertOleafToLeaf(Node *pn, uint32 i, Node *n) {
|
||||||
|
return NewLeaf(pn->key + (i << pn->pos), pn->pos, VALUE_FROM_OLEAF(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The |parent| and |leaf_next| pointers are repurposed to hold
|
||||||
|
// the next pointer in the free list.
|
||||||
|
class FreeableNodeCollector {
|
||||||
|
public:
|
||||||
|
FreeableNodeCollector() : ptr_(NULL) {}
|
||||||
|
|
||||||
|
void Add(RoutingTrie32::Node *n) {
|
||||||
|
n->leaf_next = ptr_;
|
||||||
|
ptr_ = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Revert(RoutingTrie32::Node *reset_parent_to);
|
||||||
|
void Free();
|
||||||
|
private:
|
||||||
|
RoutingTrie32::Node *ptr_;
|
||||||
|
};
|
||||||
|
|
||||||
|
void FreeableNodeCollector::Revert(RoutingTrie32::Node *reset_parent_to) {
|
||||||
|
for (RoutingTrie32::Node *p = ptr_, *pn; p != NULL; p = pn) {
|
||||||
|
pn = p->parent;
|
||||||
|
p->parent = reset_parent_to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeableNodeCollector::Free() {
|
||||||
|
for (RoutingTrie32::Node *p = ptr_, *pn; p != NULL; p = pn) {
|
||||||
|
pn = p->parent;
|
||||||
|
FreeNode(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoutingTrie32::ReplaceChild(RoutingTrie32::Node **pnp, RoutingTrie32::Node *n) {
|
||||||
|
RoutingTrie32::Node *pn = *pnp;
|
||||||
|
if (pn->parent)
|
||||||
|
RoutingTrie32::PutChild(pn->parent, (uint32)(pnp - pn->parent->child), n);
|
||||||
|
else
|
||||||
|
*pnp = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RoutingTrie32::Inflate(Node **pnp) {
|
||||||
|
Node *pn = *pnp, *n0, *n1;
|
||||||
|
FreeableNodeCollector free_on_failure, free_on_success, free_on_success_null;
|
||||||
|
Node *tn = NewNode(pn->key, pn->pos - 1, pn->bits + 1);
|
||||||
|
if (!tn)
|
||||||
|
return false;
|
||||||
|
tn->parent = pn->parent;
|
||||||
|
|
||||||
|
|
||||||
|
uint8 oleaf_compare_value = tn->pos;
|
||||||
|
|
||||||
|
for (uint32 i = 0, i_end = 1U << pn->bits; i != i_end; i++) {
|
||||||
|
Node *n = pn->child[i];
|
||||||
|
// An empty child
|
||||||
|
if (n == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (NODE_IS_OLEAF(n)) {
|
||||||
|
// Convert oleaf to leaf as parent's |pos| changed
|
||||||
|
if (!(n = ConvertOleafToLeaf(pn, i, n)))
|
||||||
|
goto nomem;
|
||||||
|
free_on_failure.Add(n);
|
||||||
|
goto insert_child;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_LEAF(n)) {
|
||||||
|
// Check whether the leaf can be converted to an oleaf.
|
||||||
|
if (n->pos == oleaf_compare_value && n->leaf_next == NULL) {
|
||||||
|
free_on_success_null.Add(n);
|
||||||
|
PutChild(tn, GET_INDEX(n->key, tn), VALUE_TO_OLEAF(n->leaf_value));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
goto insert_child;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A leaf or an internal node with skipped bits
|
||||||
|
if ((n->pos + n->bits) != pn->pos) {
|
||||||
|
insert_child:
|
||||||
|
PutChild(tn, GET_INDEX(n->key, tn), n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
free_on_success.Add(n);
|
||||||
|
// Copying oleaf from here is ok as pos is unchanged.
|
||||||
|
if (n->bits == 1) {
|
||||||
|
// An internal node with exactly two children
|
||||||
|
n0 = n->child[0];
|
||||||
|
n1 = n->child[1];
|
||||||
|
} else {
|
||||||
|
// An internal node with more than two children
|
||||||
|
if (!(n1 = NewNode(n->key | (1 << tn->pos), n->pos, n->bits - 1)))
|
||||||
|
goto nomem;
|
||||||
|
free_on_failure.Add(n1);
|
||||||
|
if (!(n0 = NewNode(n->key, n->pos, n->bits - 1)))
|
||||||
|
goto nomem;
|
||||||
|
free_on_failure.Add(n0);
|
||||||
|
uint32 j_end = 1U << (n->bits - 1);
|
||||||
|
for (uint32 j = 0; j != j_end; j++) {
|
||||||
|
PutChild(n0, j, n->child[j]);
|
||||||
|
PutChild(n1, j, n->child[j + j_end]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PutChild(tn, 2 * i + 0, n0);
|
||||||
|
PutChild(tn, 2 * i + 1, n1);
|
||||||
|
}
|
||||||
|
|
||||||
|
free_on_success.Free();
|
||||||
|
free_on_success_null.Free();
|
||||||
|
free_on_failure.Revert(NULL);
|
||||||
|
|
||||||
|
ReplaceChild(pnp, tn);
|
||||||
|
UpdateParent(tn);
|
||||||
|
FreeNode(pn);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
nomem:
|
||||||
|
free_on_success.Revert(pn);
|
||||||
|
free_on_success_null.Revert(NULL);
|
||||||
|
free_on_failure.Free();
|
||||||
|
FreeNode(tn);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RoutingTrie32::Halve(Node **pnp) {
|
||||||
|
Node *pn = *pnp, *n;
|
||||||
|
Node *tn = NewNode(pn->key, pn->pos + 1, pn->bits - 1);
|
||||||
|
FreeableNodeCollector free_on_failure, free_on_success_null;
|
||||||
|
if (!tn)
|
||||||
|
return false;
|
||||||
|
tn->parent = pn->parent;
|
||||||
|
|
||||||
|
uint8 oleaf_compare_value = tn->pos;
|
||||||
|
|
||||||
|
for (uint32 i = 0, i_end = 1U << pn->bits; i != i_end; i += 2) {
|
||||||
|
Node *n0 = pn->child[i + 0];
|
||||||
|
Node *n1 = pn->child[i + 1];
|
||||||
|
|
||||||
|
if (n0 == NULL || n1 == NULL) {
|
||||||
|
// At least one of the children is empty.
|
||||||
|
n = n0 ? n0 : n1;
|
||||||
|
|
||||||
|
if (NODE_IS_OLEAF(n)) {
|
||||||
|
// Convert oleaf to leaf as parent's |pos| changed
|
||||||
|
if (!(n = ConvertOleafToLeaf(pn, i + (n0 == NULL), n)))
|
||||||
|
goto nomem;
|
||||||
|
free_on_failure.Add(n);
|
||||||
|
} else if (n && IS_LEAF(n) && n->pos == oleaf_compare_value && n->leaf_next == NULL) {
|
||||||
|
// The leaf can be converted to an oleaf.
|
||||||
|
free_on_success_null.Add(n);
|
||||||
|
n = VALUE_TO_OLEAF(n->leaf_value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Two nonempty children
|
||||||
|
if (!(n = NewNode(pn->key + (i << pn->pos), pn->pos, 1)))
|
||||||
|
goto nomem;
|
||||||
|
free_on_failure.Add(n);
|
||||||
|
PutChild(n, 0, n0);
|
||||||
|
PutChild(n, 1, n1);
|
||||||
|
}
|
||||||
|
PutChild(tn, i / 2, n);
|
||||||
|
}
|
||||||
|
free_on_failure.Revert(NULL);
|
||||||
|
free_on_success_null.Free();
|
||||||
|
ReplaceChild(pnp, tn);
|
||||||
|
UpdateParent(tn);
|
||||||
|
FreeNode(pn);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
nomem:
|
||||||
|
free_on_failure.Free();
|
||||||
|
free_on_success_null.Revert(NULL);
|
||||||
|
FreeNode(tn);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoutingTrie32::Collapse(Node **pnp) {
|
||||||
|
Node *pn = *pnp, *n = NULL;
|
||||||
|
|
||||||
|
if (pn->empty_children != (1U << pn->bits)) {
|
||||||
|
for (uint32 i = 0; ; i++) {
|
||||||
|
n = pn->child[i];
|
||||||
|
if (n) {
|
||||||
|
if (NODE_IS_OLEAF(n)) {
|
||||||
|
if (!(n = ConvertOleafToLeaf(pn, i, n)))
|
||||||
|
return;
|
||||||
|
} else if (!IS_LEAF(n)) {
|
||||||
|
n->parent = pn->parent;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ReplaceChild(pnp, n);
|
||||||
|
FreeNode(pn);
|
||||||
|
}
|
||||||
|
|
|
@ -5,15 +5,45 @@
|
||||||
#include "tunsafe_types.h"
|
#include "tunsafe_types.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class RoutingTrie32 {
|
||||||
|
friend class FreeableNodeCollector;
|
||||||
|
public:
|
||||||
|
typedef void *Value;
|
||||||
|
struct Node;
|
||||||
|
|
||||||
|
RoutingTrie32();
|
||||||
|
~RoutingTrie32();
|
||||||
|
NOINLINE Value Lookup(uint32 ip);
|
||||||
|
NOINLINE Value LookupExact(uint32 ip, int cidr);
|
||||||
|
bool Insert(uint32 ip, int cidr, Value value);
|
||||||
|
bool Delete(uint32 ip, int cidr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Node *root_;
|
||||||
|
|
||||||
|
void Rebalance(Node *n);
|
||||||
|
bool Resize(Node *n);
|
||||||
|
bool Inflate(Node **n);
|
||||||
|
bool Halve(Node **n);
|
||||||
|
void UpdateParent(Node *n);
|
||||||
|
void ResizeChildren(Node *n);
|
||||||
|
static void Collapse(Node **n);
|
||||||
|
static void PutChild(Node *pn, uint32 i, Node *n);
|
||||||
|
static void ReplaceChild(Node **pnp, Node *n);
|
||||||
|
static Node *ConvertOleafToLeaf(Node *pn, uint32 i, Node *n);
|
||||||
|
static bool InsertLeafInto(Node **n, uint8 leaf_pos, RoutingTrie32::Value value);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Maps CIDR addresses to a peer, always returning the longest match
|
// Maps CIDR addresses to a peer, always returning the longest match
|
||||||
// Slow O(n) implementation
|
// IPv6 has a slow O(n) implementation
|
||||||
class IpToPeerMap {
|
class IpToPeerMap {
|
||||||
public:
|
public:
|
||||||
IpToPeerMap();
|
IpToPeerMap();
|
||||||
~IpToPeerMap();
|
~IpToPeerMap();
|
||||||
|
|
||||||
// Inserts an IP address of a given CIDR length into the lookup table, pointing to peer.
|
// Inserts an IP address of a given CIDR length into the lookup table, pointing to peer.
|
||||||
bool InsertV4(const void *addr, int cidr, void *peer);
|
bool InsertV4(uint32 ip, int cidr, void *peer);
|
||||||
bool InsertV6(const void *addr, int cidr, void *peer);
|
bool InsertV6(const void *addr, int cidr, void *peer);
|
||||||
|
|
||||||
// Lookup the peer matching the IP Address
|
// Lookup the peer matching the IP Address
|
||||||
|
@ -26,16 +56,12 @@ public:
|
||||||
// Remove a peer from the table
|
// Remove a peer from the table
|
||||||
void RemovePeer(void *peer);
|
void RemovePeer(void *peer);
|
||||||
private:
|
private:
|
||||||
struct Entry4 {
|
|
||||||
uint32 ip;
|
|
||||||
uint32 mask;
|
|
||||||
void *peer;
|
|
||||||
};
|
|
||||||
struct Entry6 {
|
struct Entry6 {
|
||||||
uint8 ip[16];
|
uint8 ip[16];
|
||||||
uint8 cidr_len;
|
uint8 cidr_len;
|
||||||
void *peer;
|
void *peer;
|
||||||
};
|
};
|
||||||
std::vector<Entry4> ipv4_;
|
|
||||||
std::vector<Entry6> ipv6_;
|
std::vector<Entry6> ipv6_;
|
||||||
|
|
||||||
|
RoutingTrie32 ipv4_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1155,7 +1155,7 @@ bool WgPeer::AddIp(const WgCidrAddr &cidr_addr) {
|
||||||
if (cidr_addr.cidr > 32)
|
if (cidr_addr.cidr > 32)
|
||||||
return false;
|
return false;
|
||||||
WG_ACQUIRE_RWLOCK_EXCLUSIVE(dev_->ip_to_peer_map_lock_);
|
WG_ACQUIRE_RWLOCK_EXCLUSIVE(dev_->ip_to_peer_map_lock_);
|
||||||
dev_->ip_to_peer_map_.InsertV4(cidr_addr.addr, cidr_addr.cidr, this);
|
dev_->ip_to_peer_map_.InsertV4(ReadBE32(cidr_addr.addr), cidr_addr.cidr, this);
|
||||||
WG_RELEASE_RWLOCK_EXCLUSIVE(dev_->ip_to_peer_map_lock_);
|
WG_RELEASE_RWLOCK_EXCLUSIVE(dev_->ip_to_peer_map_lock_);
|
||||||
allowed_ips_.push_back(cidr_addr);
|
allowed_ips_.push_back(cidr_addr);
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Reference in a new issue