#include <sys/param.h>
#ifdef _KERNEL
#include <sys/systm.h>
#include <sys/domain.h>
#include <sys/globaldata.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/syslog.h>
#include <sys/thread.h>
#include <net/netisr2.h>
#include <net/netmsg2.h>
#else
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <syslog.h>
#endif
#include <net/radix.h>
#ifndef _KERNEL
#undef MAXCPU
#define MAXCPU 1
#define mycpuid 0
#define log(l, ...) syslog(l, __VA_ARGS__)
#define kprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
#define print_backtrace(...)
#define panic(fmt, ...) \
do { \
fprintf(stderr, "PANIC: " fmt "\n", ##__VA_ARGS__); \
abort(); \
} while (0)
#endif
#define clen(c) (int)(*(const u_char *)(c))
static struct radix_mask *rn_mkfreelist[MAXCPU];
static struct radix_node_head *mask_rnheads[MAXCPU];
static const u_char rn_zeros[RN_MAXKEYLEN];
static const u_char rn_ones[RN_MAXKEYLEN] = RN_MAXKEYONES;
#ifdef RN_DEBUG
static int rn_nodenum;
static struct radix_node *rn_clist;
static bool rn_debug = true;
#endif
static __inline struct radix_mask *
MKGet(struct radix_mask **l)
{
struct radix_mask *m;
if (*l != NULL) {
m = *l;
*l = m->rm_next;
} else {
R_Malloc(m, struct radix_mask *, sizeof(*m));
}
return m;
}
static __inline void
MKFree(struct radix_mask **l, struct radix_mask *m)
{
m->rm_next = *l;
*l = m;
}
static struct radix_node *
rn_search(const void *_key, struct radix_node *head)
{
struct radix_node *x;
const u_char *key;
key = _key;
x = head;
while (x->rn_bit >= 0) {
if (x->rn_bmask & key[x->rn_offset])
x = x->rn_right;
else
x = x->rn_left;
}
return (x);
}
static struct radix_node *
rn_search_m(const void *_key, const void *_mask, struct radix_node *head)
{
struct radix_node *x;
const u_char *key, *mask;
key = _key;
mask = _mask;
x = head;
while (x->rn_bit >= 0) {
if ((x->rn_bmask & mask[x->rn_offset]) &&
(x->rn_bmask & key[x->rn_offset]))
x = x->rn_right;
else
x = x->rn_left;
}
return (x);
}
bool
rn_refines(const void *_m, const void *_n)
{
const u_char *m, *n, *lim, *lim2;
int longer;
bool equal;
m = _m;
n = _n;
lim2 = lim = n + clen(n);
longer = clen(n++) - clen(m++);
if (longer > 0)
lim -= longer;
equal = true;
while (n < lim) {
if (*n & ~(*m))
return (false);
if (*n++ != *m++)
equal = false;
}
while (n < lim2) {
if (*n++)
return (false);
}
if (equal && (longer < 0)) {
lim2 = m - longer;
while (m < lim2) {
if (*m++)
return (true);
}
}
return (!equal);
}
struct radix_node *
rn_lookup(const void *_key, const void *_mask, struct radix_node_head *head)
{
struct radix_node *x;
const u_char *key, *mask, *netmask;
key = _key;
mask = _mask;
netmask = NULL;
if (mask != NULL) {
x = rn_addmask(mask, true, head->rnh_treetop->rn_offset,
head->rnh_maskhead);
if (x == NULL)
return (NULL);
netmask = x->rn_key;
}
x = rn_match(key, head);
if (x != NULL && netmask != NULL) {
while (x != NULL && x->rn_mask != netmask)
x = x->rn_dupedkey;
}
return (x);
}
static bool
rn_satisfies_leaf(const void *key, struct radix_node *leaf, int skip)
{
const u_char *cp, *cp2, *cp3, *cplim;
int length;
cp = key;
cp2 = leaf->rn_key;
cp3 = leaf->rn_mask;
length = MIN(clen(cp), clen(cp2));
if (cp3 == NULL)
cp3 = rn_ones;
else
length = MIN(length, clen(cp3));
cplim = cp + length;
cp2 += skip;
cp3 += skip;
for (cp += skip; cp < cplim; cp++, cp2++, cp3++) {
if ((*cp ^ *cp2) & *cp3)
return (false);
}
return (true);
}
struct radix_node *
rn_match(const void *key, struct radix_node_head *head)
{
struct radix_node *top, *t, *saved_t;
const u_char *cp, *cp2, *cplim;
int klen, matched_off, test, bit, rn_bit;
top = head->rnh_treetop;
t = rn_search(key, top);
if (t->rn_mask != NULL)
klen = clen(t->rn_mask);
else
klen = clen(key);
cplim = (const u_char *)key + klen;
cp = (const u_char *)key + top->rn_offset;
cp2 = t->rn_key + top->rn_offset;
for (; cp < cplim; cp++, cp2++) {
if (*cp != *cp2)
goto on1;
}
if (t->rn_flags & RNF_ROOT)
t = t->rn_dupedkey;
return (t);
on1:
test = (*cp ^ *cp2) & 0xff;
for (bit = 7; (test >>= 1) > 0;)
bit--;
matched_off = cp - (const u_char *)key;
bit += matched_off << 3;
rn_bit = -1 - bit;
saved_t = t;
if (t->rn_mask == NULL)
t = t->rn_dupedkey;
for (; t != NULL; t = t->rn_dupedkey) {
if (t->rn_flags & RNF_NORMAL) {
if (rn_bit <= t->rn_bit)
return (t);
} else if (rn_satisfies_leaf(key, t, matched_off))
return (t);
}
t = saved_t;
do {
struct radix_node *x;
struct radix_mask *m;
int skip;
t = t->rn_parent;
for (m = t->rn_mklist; m != NULL; m = m->rm_next) {
if (m->rm_flags & RNF_NORMAL) {
if (rn_bit <= m->rm_bit)
return (m->rm_leaf);
} else {
skip = MIN(t->rn_offset, matched_off);
x = rn_search_m(key, m->rm_mask, t);
while (x != NULL && x->rn_mask != m->rm_mask)
x = x->rn_dupedkey;
if (x != NULL &&
rn_satisfies_leaf(key, x, skip))
return (x);
}
}
} while (t != top);
return (NULL);
}
static struct radix_node *
rn_newpair(const void *key, int bit, struct radix_node nodes[2])
{
struct radix_node *left, *parent;
left = &nodes[0];
parent = &nodes[1];
parent->rn_bit = bit;
parent->rn_bmask = 0x80 >> (bit & 0x7);
parent->rn_offset = bit >> 3;
parent->rn_left = left;
parent->rn_flags = RNF_ACTIVE;
parent->rn_mklist = NULL;
left->rn_bit = -1;
left->rn_key = key;
left->rn_parent = parent;
left->rn_flags = parent->rn_flags;
left->rn_mklist = NULL;
#ifdef RN_DEBUG
left->rn_info = rn_nodenum++;
parent->rn_info = rn_nodenum++;
left->rn_twin = parent;
left->rn_ybro = rn_clist;
rn_clist = left;
#endif
return (parent);
}
static struct radix_node *
rn_insert(const void *key, struct radix_node_head *head, bool *dupentry,
struct radix_node nodes[2])
{
struct radix_node *top, *t, *tt;
const u_char *cp;
unsigned int bit;
int head_off, klen;
top = head->rnh_treetop;
head_off = top->rn_offset;
klen = clen(key);
cp = (const u_char *)key + head_off;
t = rn_search(key, top);
{
const u_char *cp2 = t->rn_key + head_off;
const u_char *cplim = (const u_char *)key + klen;
int cmp_res;
while (cp < cplim) {
if (*cp2++ != *cp++)
goto on1;
}
*dupentry = true;
return (t);
on1:
*dupentry = false;
cmp_res = (cp[-1] ^ cp2[-1]) & 0xff;
for (bit = (cp - (const u_char *)key) << 3; cmp_res; bit--)
cmp_res >>= 1;
}
{
struct radix_node *p, *x = top;
cp = key;
do {
p = x;
if (cp[x->rn_offset] & x->rn_bmask)
x = x->rn_right;
else
x = x->rn_left;
} while (bit > (unsigned int)x->rn_bit);
#ifdef RN_DEBUG
if (rn_debug) {
log(LOG_DEBUG, "%s: Going In:\n", __func__);
traverse(p);
}
#endif
t = rn_newpair(key, bit, nodes);
tt = t->rn_left;
if ((cp[p->rn_offset] & p->rn_bmask) == 0)
p->rn_left = t;
else
p->rn_right = t;
x->rn_parent = t;
t->rn_parent = p;
if ((cp[t->rn_offset] & t->rn_bmask) == 0) {
t->rn_right = x;
} else {
t->rn_right = tt;
t->rn_left = x;
}
#ifdef RN_DEBUG
if (rn_debug) {
log(LOG_DEBUG, "%s: Coming Out:\n", __func__);
traverse(p);
}
#endif
}
return (tt);
}
struct radix_node *
rn_addmask(const void *_mask, bool search, int skip,
struct radix_node_head *maskhead)
{
struct radix_node *x, *saved_x;
const u_char *mask, *cp, *cplim;
u_char *p, addmask_key[RN_MAXKEYLEN];
int bit, mlen;
bool maskduplicated, isnormal;
mask = _mask;
if ((mlen = clen(mask)) > RN_MAXKEYLEN)
mlen = RN_MAXKEYLEN;
if (skip == 0)
skip = 1;
if (mlen <= skip)
return (maskhead->rnh_nodes);
bzero(addmask_key, sizeof(addmask_key));
if (skip > 1)
bcopy(rn_ones + 1, addmask_key + 1, skip - 1);
bcopy(mask + skip, addmask_key + skip, mlen - skip);
for (cp = addmask_key + mlen; (cp > addmask_key) && cp[-1] == 0;)
cp--;
mlen = cp - addmask_key;
if (mlen <= skip)
return (maskhead->rnh_nodes);
*addmask_key = mlen;
x = rn_search(addmask_key, maskhead->rnh_treetop);
if (x->rn_key == NULL) {
kprintf("WARNING: radix_node->rn_key is NULL rn=%p\n", x);
print_backtrace(-1);
x = NULL;
} else if (bcmp(addmask_key, x->rn_key, mlen) != 0) {
x = NULL;
}
if (x != NULL || search)
return (x);
R_Malloc(x, struct radix_node *, RN_MAXKEYLEN + 2 * (sizeof *x));
if ((saved_x = x) == NULL)
return (NULL);
bzero(x, RN_MAXKEYLEN + 2 * (sizeof *x));
mask = p = (u_char *)(x + 2);
bcopy(addmask_key, p, mlen);
x = rn_insert(mask, maskhead, &maskduplicated, x);
if (maskduplicated) {
log(LOG_ERR, "%s: mask impossibly already in tree", __func__);
R_Free(saved_x);
return (x);
}
bit = 0;
isnormal = true;
cplim = mask + mlen;
for (cp = mask + skip; cp < cplim; cp++) {
if (*cp != 0xff)
break;
}
if (cp != cplim) {
static const u_char normal_chars[] = {
0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
};
u_char j;
for (j = 0x80; (j & *cp) != 0; j >>= 1)
bit++;
if (cp != (cplim - 1) || *cp != normal_chars[bit])
isnormal = false;
}
bit += (cp - mask) << 3;
x->rn_bit = -1 - bit;
if (isnormal)
x->rn_flags |= RNF_NORMAL;
return (x);
}
static bool
rn_lexobetter(const void *_m, const void *_n)
{
const u_char *m, *n, *lim;
m = _m;
n = _n;
if (clen(m) > clen(n)) {
return (true);
}
if (clen(m) == clen(n)) {
for (lim = m + clen(m); m < lim; m++, n++) {
if (*m > *n)
return (true);
}
}
return (false);
}
static struct radix_mask *
rn_new_radix_mask(struct radix_node *node, struct radix_mask *nextmask)
{
struct radix_mask *m;
m = MKGet(&rn_mkfreelist[mycpuid]);
if (m == NULL) {
log(LOG_ERR, "Mask for route not entered\n");
return (NULL);
}
bzero(m, sizeof(*m));
m->rm_bit = node->rn_bit;
m->rm_flags = node->rn_flags;
if (m->rm_flags & RNF_NORMAL)
m->rm_leaf = node;
else
m->rm_mask = node->rn_mask;
m->rm_next = nextmask;
node->rn_mklist = m;
return (m);
}
struct radix_node *
rn_addroute(const void *key, const void *mask,
struct radix_node_head *head, struct radix_node nodes[2])
{
struct radix_node *top, *t, *x, *tt, *saved_tt;
struct radix_mask *m, **mp;
int bit, bit_leaf;
bool keyduplicated;
const void *mmask;
top = head->rnh_treetop;
x = NULL;
bit = bit_leaf = 0;
if (mask != NULL) {
if ((x = rn_addmask(mask, false, top->rn_offset,
head->rnh_maskhead)) == NULL)
return (NULL);
bit_leaf = x->rn_bit;
bit = -1 - x->rn_bit;
mask = x->rn_key;
}
saved_tt = tt = rn_insert(key, head, &keyduplicated, nodes);
if (keyduplicated) {
for (t = tt; tt != NULL; t = tt, tt = tt->rn_dupedkey) {
if (tt->rn_mask == mask)
return (NULL);
if (mask == NULL ||
(tt->rn_mask != NULL &&
((bit_leaf < tt->rn_bit)
|| rn_refines(mask, tt->rn_mask)
|| rn_lexobetter(mask, tt->rn_mask))))
break;
}
if (tt == saved_tt) {
struct radix_node *xx = x;
(tt = nodes)->rn_dupedkey = t;
tt->rn_flags = t->rn_flags;
tt->rn_parent = x = t->rn_parent;
t->rn_parent = tt;
if (x->rn_left == t)
x->rn_left = tt;
else
x->rn_right = tt;
saved_tt = tt; x = xx;
} else {
(tt = nodes)->rn_dupedkey = t->rn_dupedkey;
t->rn_dupedkey = tt;
tt->rn_parent = t;
if (tt->rn_dupedkey != NULL)
tt->rn_dupedkey->rn_parent = tt;
}
tt->rn_key = key;
tt->rn_bit = -1;
tt->rn_flags = RNF_ACTIVE;
#ifdef RN_DEBUG
tt->rn_info = rn_nodenum++;
tt->rn_twin = tt + 1;
tt->rn_twin->rn_info = rn_nodenum++;
tt->rn_ybro = rn_clist;
rn_clist = tt;
#endif
}
if (mask != NULL) {
tt->rn_mask = mask;
tt->rn_bit = x->rn_bit;
tt->rn_flags |= x->rn_flags & RNF_NORMAL;
}
t = saved_tt->rn_parent;
if (keyduplicated)
goto on2;
bit_leaf = -1 - t->rn_bit;
if (t->rn_right == saved_tt)
x = t->rn_left;
else
x = t->rn_right;
if (x->rn_bit < 0) {
mp = &t->rn_mklist;
while (x != NULL) {
if (x->rn_mask != NULL &&
x->rn_bit >= bit_leaf &&
x->rn_mklist == NULL) {
*mp = m = rn_new_radix_mask(x, NULL);
if (m != NULL)
mp = &m->rm_next;
}
x = x->rn_dupedkey;
}
} else if (x->rn_mklist != NULL) {
for (mp = &x->rn_mklist; (m = *mp) != NULL; mp = &m->rm_next) {
if (m->rm_bit >= bit_leaf)
break;
}
t->rn_mklist = m;
*mp = NULL;
}
on2:
if (mask == NULL || bit > t->rn_bit)
return (tt);
bit_leaf = tt->rn_bit;
do {
x = t;
t = t->rn_parent;
} while (bit <= t->rn_bit && x != top);
for (mp = &x->rn_mklist; (m = *mp) != NULL; mp = &m->rm_next) {
if (m->rm_bit < bit_leaf)
continue;
if (m->rm_bit > bit_leaf)
break;
if (m->rm_flags & RNF_NORMAL) {
mmask = m->rm_leaf->rn_mask;
if (tt->rn_flags & RNF_NORMAL) {
log(LOG_ERR,
"Non-unique normal route, mask not entered\n");
return (tt);
}
} else
mmask = m->rm_mask;
if (mmask == mask) {
m->rm_refs++;
tt->rn_mklist = m;
return (tt);
}
if (rn_refines(mask, mmask) || rn_lexobetter(mask, mmask))
break;
}
*mp = rn_new_radix_mask(tt, *mp);
return (tt);
}
struct radix_node *
rn_delete(const void *key, const void *mask, struct radix_node_head *head)
{
struct radix_node *top, *t, *p, *x, *tt, *saved_tt, *dupedkey;
struct radix_mask *m, *saved_m, **mp;
int bit, head_off, klen, cpu;
cpu = mycpuid;
x = head->rnh_treetop;
tt = rn_search(key, x);
head_off = x->rn_offset;
klen = clen(key);
saved_tt = tt;
top = x;
if (tt == NULL ||
bcmp((const u_char *)key + head_off, tt->rn_key + head_off,
klen - head_off) != 0)
return (NULL);
if (mask != NULL) {
if ((x = rn_addmask(mask, true, head_off,
head->rnh_maskhead)) == NULL)
return (NULL);
mask = x->rn_key;
while (tt->rn_mask != mask) {
if ((tt = tt->rn_dupedkey) == NULL)
return (NULL);
}
}
if (tt->rn_mask == NULL || (saved_m = m = tt->rn_mklist) == NULL)
goto on1;
if (tt->rn_flags & RNF_NORMAL) {
if (m->rm_leaf != tt || m->rm_refs > 0) {
log(LOG_ERR, "rn_delete: inconsistent annotation\n");
return (NULL);
}
} else {
if (m->rm_mask != tt->rn_mask) {
log(LOG_ERR, "rn_delete: inconsistent annotation\n");
goto on1;
}
if (--m->rm_refs >= 0)
goto on1;
}
bit = -1 - tt->rn_bit;
t = saved_tt->rn_parent;
if (bit > t->rn_bit)
goto on1;
do {
x = t;
t = t->rn_parent;
} while (bit <= t->rn_bit && x != top);
for (mp = &x->rn_mklist; (m = *mp) != NULL; mp = &m->rm_next)
if (m == saved_m) {
*mp = m->rm_next;
MKFree(&rn_mkfreelist[cpu], m);
break;
}
if (m == NULL) {
log(LOG_ERR, "rn_delete: couldn't find our annotation\n");
if (tt->rn_flags & RNF_NORMAL)
return (NULL);
}
on1:
if (tt->rn_flags & RNF_ROOT)
return (NULL);
#ifdef RN_DEBUG
for (t = rn_clist; t != NULL && t->rn_ybro != tt; t = t->rn_ybro)
;
if (t != NULL)
t->rn_ybro = tt->rn_ybro;
#endif
t = tt->rn_parent;
dupedkey = saved_tt->rn_dupedkey;
if (dupedkey != NULL) {
if (tt == saved_tt) {
x = dupedkey;
x->rn_parent = t;
if (t->rn_left == tt)
t->rn_left = x;
else
t->rn_right = x;
} else {
for (x = p = saved_tt; p != NULL && p->rn_dupedkey != tt;)
p = p->rn_dupedkey;
if (p) {
p->rn_dupedkey = tt->rn_dupedkey;
if (tt->rn_dupedkey)
tt->rn_dupedkey->rn_parent = p;
} else {
log(LOG_ERR, "rn_delete: couldn't find us\n");
}
}
t = tt + 1;
if (t->rn_flags & RNF_ACTIVE) {
#ifndef RN_DEBUG
*++x = *t;
p = t->rn_parent;
#else
bit = t->rn_info;
*++x = *t;
t->rn_info = bit;
p = t->rn_parent;
#endif
if (p->rn_left == t)
p->rn_left = x;
else
p->rn_right = x;
x->rn_left->rn_parent = x;
x->rn_right->rn_parent = x;
}
goto out;
}
if (t->rn_left == tt)
x = t->rn_right;
else
x = t->rn_left;
p = t->rn_parent;
if (p->rn_right == t)
p->rn_right = x;
else
p->rn_left = x;
x->rn_parent = p;
if (t->rn_mklist != NULL) {
if (x->rn_bit >= 0) {
for (mp = &x->rn_mklist; (m = *mp) != NULL;)
mp = &m->rm_next;
*mp = t->rn_mklist;
} else {
for (m = t->rn_mklist; m && x; x = x->rn_dupedkey)
if (m == x->rn_mklist) {
struct radix_mask *mm = m->rm_next;
x->rn_mklist = NULL;
if (--(m->rm_refs) < 0)
MKFree(&rn_mkfreelist[cpu], m);
m = mm;
}
if (m) {
log(LOG_ERR,
"rn_delete: Orphaned Mask %p at %p\n",
(void *)m, (void *)x);
}
}
}
x = tt + 1;
if (t != x) {
#ifndef RN_DEBUG
*t = *x;
#else
bit = t->rn_info;
*t = *x;
t->rn_info = bit;
#endif
t->rn_left->rn_parent = t;
t->rn_right->rn_parent = t;
p = x->rn_parent;
if (p->rn_left == x)
p->rn_left = t;
else
p->rn_right = t;
}
out:
tt[0].rn_flags &= ~RNF_ACTIVE;
tt[1].rn_flags &= ~RNF_ACTIVE;
return (tt);
}
static int
rn_walktree_from(struct radix_node_head *h, const void *_addr,
const void *_mask, walktree_f_t *f, void *w)
{
struct radix_node *rn, *base, *next, *last;
const u_char *addr, *mask;
bool stopping;
int lastb, error;
addr = _addr;
mask = _mask;
last = NULL;
stopping = false;
for (rn = h->rnh_treetop; rn->rn_bit >= 0; ) {
last = rn;
if (!(rn->rn_bmask & mask[rn->rn_offset])) {
break;
}
if (rn->rn_bmask & addr[rn->rn_offset]) {
rn = rn->rn_right;
} else {
rn = rn->rn_left;
}
}
rn = last;
lastb = rn->rn_bit;
while (rn->rn_bit >= 0)
rn = rn->rn_left;
while (!stopping) {
base = rn;
while (rn->rn_parent->rn_right == rn &&
!(rn->rn_flags & RNF_ROOT)) {
rn = rn->rn_parent;
if (rn->rn_bit < lastb) {
stopping = true;
}
}
for (rn = rn->rn_parent->rn_right; rn->rn_bit >= 0;)
rn = rn->rn_left;
next = rn;
while ((rn = base) != NULL) {
base = rn->rn_dupedkey;
if (!(rn->rn_flags & RNF_ROOT) && (error = (*f)(rn, w)))
return (error);
}
rn = next;
if (rn->rn_flags & RNF_ROOT) {
stopping = true;
}
}
return 0;
}
static int
rn_walktree_at(struct radix_node_head *h, const void *addr, const void *mask,
walktree_f_t *f, void *w)
{
struct radix_node *rn, *base, *next;
int error;
rn = h->rnh_treetop;
if (addr == NULL) {
while (rn->rn_bit >= 0)
rn = rn->rn_left;
} else {
if (mask != NULL)
rn = rn_search_m(addr, mask, rn);
else
rn = rn_search(addr, rn);
}
for (;;) {
base = rn;
while (rn->rn_parent->rn_right == rn &&
!(rn->rn_flags & RNF_ROOT))
rn = rn->rn_parent;
for (rn = rn->rn_parent->rn_right; rn->rn_bit >= 0;)
rn = rn->rn_left;
next = rn;
while ((rn = base)) {
base = rn->rn_dupedkey;
if (!(rn->rn_flags & RNF_ROOT) && (error = (*f)(rn, w)))
return (error);
}
rn = next;
if (rn->rn_flags & RNF_ROOT)
return (0);
}
}
static int
rn_walktree(struct radix_node_head *h, walktree_f_t *f, void *w)
{
return rn_walktree_at(h, NULL, NULL, f, w);
}
int
rn_inithead(struct radix_node_head **head, struct radix_node_head *maskhead,
int off_bytes)
{
struct radix_node_head *rnh;
struct radix_node *root, *left, *right;
if (*head != NULL)
return (1);
R_Malloc(rnh, struct radix_node_head *, sizeof *rnh);
if (rnh == NULL)
return (0);
if (maskhead == NULL)
off_bytes = 0;
if (off_bytes >= RN_MAXKEYLEN)
panic("%s: invalid off_bytes=%d", __func__, off_bytes);
bzero(rnh, sizeof *rnh);
*head = rnh;
root = rn_newpair(rn_zeros, off_bytes * NBBY, rnh->rnh_nodes);
right = &rnh->rnh_nodes[2];
root->rn_parent = root;
root->rn_flags = RNF_ROOT | RNF_ACTIVE;
root->rn_right = right;
left = root->rn_left;
left->rn_bit = -1 - off_bytes * NBBY;
left->rn_flags = root->rn_flags;
*right = *left;
right->rn_key = rn_ones;
rnh->rnh_treetop = root;
rnh->rnh_maskhead = maskhead;
rnh->rnh_addaddr = rn_addroute;
rnh->rnh_deladdr = rn_delete;
rnh->rnh_matchaddr = rn_match;
rnh->rnh_lookup = rn_lookup;
rnh->rnh_walktree = rn_walktree;
rnh->rnh_walktree_from = rn_walktree_from;
rnh->rnh_walktree_at = rn_walktree_at;
return (1);
}
void
rn_freemask(struct radix_node *rn)
{
if (rn->rn_mask != NULL)
panic("%s: not a mask node", __func__);
R_Free(rn);
}
struct rn_flush_ctx {
struct radix_node_head *head;
freenode_f_t *f;
};
static int
rn_flush_walker(struct radix_node *rn, void *arg)
{
struct rn_flush_ctx *ctx = arg;
struct radix_node *node;
node = ctx->head->rnh_deladdr(rn->rn_key, rn->rn_mask, ctx->head);
if (node != rn) {
panic("%s: deleted wrong node: %p, want: %p",
__func__, node, rn);
}
if (ctx->f)
ctx->f(rn);
return 0;
}
#define IS_EMPTY(head) \
(((head)->rnh_treetop == &(head)->rnh_nodes[1]) && \
((head)->rnh_treetop->rn_left == &(head)->rnh_nodes[0]) && \
((head)->rnh_treetop->rn_right == &(head)->rnh_nodes[2]))
void
rn_flush(struct radix_node_head *head, freenode_f_t *f)
{
struct rn_flush_ctx ctx;
if (f == rn_freemask && head->rnh_maskhead != NULL)
panic("%s: rn_freemask() used with non-mask tree", __func__);
ctx.head = head;
ctx.f = f;
head->rnh_walktree(head, rn_flush_walker, &ctx);
if (!IS_EMPTY(head))
panic("%s: failed to flush all nodes", __func__);
}
void
rn_freehead(struct radix_node_head *head)
{
if (!IS_EMPTY(head))
panic("%s: radix tree not empty", __func__);
R_Free(head);
}
#ifdef _KERNEL
static void
rn_init_handler(netmsg_t msg)
{
int cpu = mycpuid;
ASSERT_NETISR_NCPUS(cpu);
if (rn_inithead(&mask_rnheads[cpu], NULL, 0) == 0)
panic("%s: failed to create mask tree", __func__);
netisr_forwardmsg(&msg->base, cpu + 1);
}
void
rn_init(void)
{
struct netmsg_base msg;
struct domain *dom;
SLIST_FOREACH(dom, &domains, dom_next) {
if (dom->dom_maxrtkey > RN_MAXKEYLEN) {
panic("domain %s maxkey too big %d/%d",
dom->dom_name, dom->dom_maxrtkey, RN_MAXKEYLEN);
}
}
netmsg_init(&msg, NULL, &curthread->td_msgport, 0, rn_init_handler);
netisr_domsg_global(&msg);
}
struct radix_node_head *
rn_cpumaskhead(int cpu)
{
ASSERT_NETISR_NCPUS(cpu);
KKASSERT(mask_rnheads[cpu] != NULL);
return mask_rnheads[cpu];
}
#else
void
rn_init(void)
{
if (rn_inithead(&mask_rnheads[0], NULL, 0) == 0)
panic("%s: failed to create mask tree", __func__);
}
struct radix_node_head *
rn_cpumaskhead(int cpu __unused)
{
return mask_rnheads[0];
}
#endif