1114 lines
24 KiB
C
Executable File
1114 lines
24 KiB
C
Executable File
/*************************************************************************
|
|
*
|
|
* ivi_rule.c :
|
|
*
|
|
* MAP-T/MAP-E 4to6 Prefix Mapping Kernel Module
|
|
*
|
|
* Copyright (C) 2013 CERNET Network Center
|
|
* All rights reserved.
|
|
*
|
|
* Design and coding:
|
|
* Xing Li <xing@cernet.edu.cn>
|
|
* Congxiao Bao <congxiao@cernet.edu.cn>
|
|
* Wentao Shang <wentaoshang@gmail.com>
|
|
* Yuncheng Zhu <haoyu@cernet.edu.cn>
|
|
* Guoliang Han <bupthgl@gmail.com>
|
|
*
|
|
* Contributions:
|
|
*
|
|
* This file is part of MAP-T/MAP-E Kernel Module.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with MAP-T/MAP-E Kernel Module. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*
|
|
* For more versions, please send an email to <bupthgl@gmail.com> to
|
|
* obtain an password to access the svn server.
|
|
*
|
|
* LIC: GPLv2
|
|
*
|
|
************************************************************************/
|
|
|
|
#include "ivi_rule.h"
|
|
|
|
#define KEYLENGTH 32
|
|
|
|
typedef u32 t_key;
|
|
|
|
#define T_TNODE 0
|
|
#define T_LEAF 1
|
|
#define NODE_TYPE_MASK 0x1UL
|
|
#define NODE_TYPE(node) ((node)->parent & NODE_TYPE_MASK)
|
|
|
|
#define IS_TNODE(n) (!(n->parent & T_LEAF))
|
|
#define IS_LEAF(n) (n->parent & T_LEAF)
|
|
|
|
static const int halve_threshold = 25;
|
|
static const int inflate_threshold = 50;
|
|
static const int halve_threshold_root = 15;
|
|
static const int inflate_threshold_root = 30;
|
|
|
|
struct tentry {
|
|
unsigned long parent;
|
|
t_key key;
|
|
};
|
|
|
|
struct tnode {
|
|
unsigned long parent;
|
|
t_key key;
|
|
unsigned char pos;
|
|
unsigned char bits;
|
|
unsigned int full_children;
|
|
unsigned int empty_children;
|
|
struct tentry *child[0];
|
|
};
|
|
|
|
struct tleaf_info {
|
|
struct hlist_node node;
|
|
int plen;
|
|
u32 mask_plen;
|
|
struct in6_addr prefix6;
|
|
int prefix6_len;
|
|
u16 ratio;
|
|
u16 adjacent;
|
|
u8 format;
|
|
u8 transport;
|
|
};
|
|
|
|
struct tleaf {
|
|
unsigned long parent;
|
|
t_key key;
|
|
struct hlist_head head;
|
|
};
|
|
|
|
static struct tentry *trie = NULL;
|
|
static spinlock_t trie_lock;
|
|
|
|
#ifdef IVI_DEBUG
|
|
/* Memory counter */
|
|
static int balance = 0;
|
|
#endif
|
|
|
|
static inline struct tnode* node_parent(const struct tentry *node)
|
|
{
|
|
return (struct tnode *)(node->parent & ~NODE_TYPE_MASK);
|
|
}
|
|
|
|
static inline void node_set_parent(struct tentry *node, const struct tnode *ptr)
|
|
{
|
|
node->parent = (unsigned long)ptr | NODE_TYPE(node);
|
|
}
|
|
|
|
static inline struct tentry* tnode_get_child(const struct tnode *tn, unsigned int i)
|
|
{
|
|
return (tn->child[i]);
|
|
}
|
|
|
|
static inline int tnode_child_length(const struct tnode *tn)
|
|
{
|
|
return 1 << tn->bits;
|
|
}
|
|
|
|
static inline t_key mask_pfx(t_key k, unsigned int l)
|
|
{
|
|
return (l == 0) ? 0 : k >> (KEYLENGTH-l) << (KEYLENGTH-l);
|
|
}
|
|
|
|
static inline t_key tkey_extract_bits(t_key a, unsigned int offset, unsigned int bits)
|
|
{
|
|
if (offset < KEYLENGTH)
|
|
return ((t_key)(a << offset)) >> (KEYLENGTH - bits);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static inline int tkey_equals(t_key a, t_key b)
|
|
{
|
|
return a == b;
|
|
}
|
|
|
|
static inline int tkey_sub_equals(t_key a, int offset, int bits, t_key b)
|
|
{
|
|
if (bits == 0 || offset >= KEYLENGTH)
|
|
return 1;
|
|
bits = bits > KEYLENGTH ? KEYLENGTH : bits;
|
|
return ((a ^ b) << offset) >> (KEYLENGTH - bits) == 0;
|
|
}
|
|
|
|
static inline int tkey_mismatch(t_key a, int offset, t_key b)
|
|
{
|
|
t_key diff = a ^ b;
|
|
int i = offset;
|
|
|
|
if (!diff)
|
|
return 0;
|
|
while ((diff << i) >> (KEYLENGTH-1) == 0)
|
|
i++;
|
|
return i;
|
|
}
|
|
|
|
/* Caller must free or store all the children of tnode 'n'
|
|
* before calling this function to free it.
|
|
*/
|
|
static inline void tnode_free(struct tnode *n)
|
|
{
|
|
if (!n)
|
|
return;
|
|
|
|
kfree(n);
|
|
#ifdef IVI_DEBUG
|
|
balance--;
|
|
#endif
|
|
}
|
|
|
|
static inline void tleaf_info_free(struct tleaf_info *li)
|
|
{
|
|
if (!li)
|
|
return;
|
|
|
|
kfree(li);
|
|
#ifdef IVI_DEBUG
|
|
balance--;
|
|
#endif
|
|
}
|
|
|
|
static inline void tleaf_free(struct tleaf *l)
|
|
{
|
|
if (!l)
|
|
return;
|
|
|
|
kfree(l);
|
|
#ifdef IVI_DEBUG
|
|
balance--;
|
|
#endif
|
|
}
|
|
|
|
static void tentry_free(struct tentry *node)
|
|
{
|
|
if (!node)
|
|
return;
|
|
|
|
if (IS_LEAF(node))
|
|
tleaf_free((struct tleaf *)node);
|
|
else
|
|
tnode_free((struct tnode *)node);
|
|
}
|
|
|
|
static void tnode_clean_free(struct tnode *tn)
|
|
{
|
|
int i;
|
|
struct tentry *tofree;
|
|
|
|
if (!tn)
|
|
return;
|
|
|
|
for (i = 0; i < tnode_child_length(tn); i++) {
|
|
tofree = tn->child[i];
|
|
if (tofree)
|
|
tentry_free(tofree);
|
|
}
|
|
tnode_free(tn);
|
|
}
|
|
|
|
static struct tleaf *tleaf_new(void)
|
|
{
|
|
struct tleaf *l = (struct tleaf *)kmalloc(sizeof(struct tleaf), GFP_ATOMIC);
|
|
#ifdef IVI_DEBUG
|
|
balance++;
|
|
#endif
|
|
if (l) {
|
|
l->parent = T_LEAF;
|
|
INIT_HLIST_HEAD(&l->head);
|
|
}
|
|
return l;
|
|
}
|
|
|
|
static struct tleaf_info *tleaf_info_new(int plen)
|
|
{
|
|
struct tleaf_info *li = (struct tleaf_info *)kzalloc(sizeof(struct tleaf_info), GFP_ATOMIC);
|
|
#ifdef IVI_DEBUG
|
|
balance++;
|
|
#endif
|
|
if (li) {
|
|
li->plen = plen;
|
|
li->mask_plen = ntohl(inet_make_mask(plen));
|
|
INIT_HLIST_NODE(&li->node);
|
|
}
|
|
return li;
|
|
}
|
|
|
|
|
|
static struct tnode *tnode_new(t_key key, int pos, int bits)
|
|
{
|
|
size_t size = sizeof(struct tnode) + (sizeof(struct tentry *) << bits);
|
|
struct tnode *tn = (struct tnode *)kzalloc(size, GFP_ATOMIC);
|
|
#ifdef IVI_DEBUG
|
|
balance++;
|
|
#endif
|
|
|
|
if (tn) {
|
|
tn->parent = T_TNODE;
|
|
tn->pos = pos;
|
|
tn->bits = bits;
|
|
tn->key = key;
|
|
tn->full_children = 0;
|
|
tn->empty_children = 1 << bits;
|
|
}
|
|
|
|
return tn;
|
|
}
|
|
|
|
/*
|
|
* Check whether a tnode 'chi' is "full", i.e. it is an internal node
|
|
* and no bits are skipped.
|
|
*/
|
|
static inline int tnode_full(const struct tnode *tn, const struct tentry *chi)
|
|
{
|
|
struct tnode *n;
|
|
|
|
if (chi == NULL || IS_LEAF(chi))
|
|
return 0;
|
|
|
|
n = (struct tnode *)chi;
|
|
|
|
return n->pos == tn->pos + tn->bits;
|
|
}
|
|
|
|
/*
|
|
* Add a child at position i overwriting the old value.
|
|
* Caller must store the old pointer value if it is not NULL,
|
|
* otherwise the old memory will be lost.
|
|
* Update the value of full_children and empty_children.
|
|
*/
|
|
static void tnode_put_child_reorg(struct tnode *tn, int i, struct tentry *n, int wasfull)
|
|
{
|
|
struct tentry *chi = tn->child[i];
|
|
int isfull;
|
|
|
|
/* update emptyChildren */
|
|
if (n == NULL && chi != NULL)
|
|
tn->empty_children++;
|
|
else if (n != NULL && chi == NULL)
|
|
tn->empty_children--;
|
|
|
|
/* update fullChildren */
|
|
if (wasfull == -1)
|
|
wasfull = tnode_full(tn, chi);
|
|
|
|
isfull = tnode_full(tn, n);
|
|
if (wasfull && !isfull)
|
|
tn->full_children--;
|
|
else if (!wasfull && isfull)
|
|
tn->full_children++;
|
|
|
|
if (n)
|
|
node_set_parent(n, tn);
|
|
|
|
tn->child[i] = n;
|
|
}
|
|
|
|
static inline void put_child(struct tnode *tn, int i, struct tentry *n)
|
|
{
|
|
tnode_put_child_reorg(tn, i, n, -1);
|
|
}
|
|
|
|
static struct tentry *resize(struct tnode *tn);
|
|
static struct tnode *inflate(struct tnode *tn);
|
|
static struct tnode *halve(struct tnode *tn);
|
|
|
|
static struct tnode *inflate(struct tnode *tn)
|
|
{
|
|
struct tnode *oldtnode = tn;
|
|
int olen = tnode_child_length(tn);
|
|
int i;
|
|
|
|
tn = tnode_new(oldtnode->key, oldtnode->pos, oldtnode->bits + 1);
|
|
|
|
if (!tn)
|
|
return NULL;
|
|
|
|
/* Create new internal tnode if necessary, with its children left empty */
|
|
for (i = 0; i < olen; i++) {
|
|
struct tnode *inode;
|
|
inode = (struct tnode *)tnode_get_child(oldtnode, i);
|
|
if (tnode_full(oldtnode, (struct tentry *)inode) && inode->bits > 1) {
|
|
struct tnode *left, *right;
|
|
t_key m;
|
|
|
|
m = ~0U << (KEYLENGTH - 1) >> inode->pos;
|
|
|
|
left = tnode_new(inode->key & (~m), inode->pos + 1, inode->bits - 1);
|
|
if (!left) {
|
|
tnode_free(tn);
|
|
return NULL;
|
|
}
|
|
|
|
right = tnode_new(inode->key | m, inode->pos + 1, inode->bits - 1);
|
|
if (!right) {
|
|
tnode_free(left);
|
|
tnode_free(tn);
|
|
return NULL;
|
|
}
|
|
|
|
/* Insert the *doubled* internal tnodes */
|
|
put_child(tn, 2*i, (struct tentry *)left);
|
|
put_child(tn, 2*i+1, (struct tentry *)right);
|
|
}
|
|
}
|
|
|
|
/* Fill in the children of the *doubled* internal tnodes */
|
|
for (i = 0; i < olen; i++) {
|
|
struct tnode *inode;
|
|
struct tentry *node = tnode_get_child(oldtnode, i);
|
|
struct tnode *left, *right;
|
|
int size, j;
|
|
|
|
/* An empty child */
|
|
if (node == NULL)
|
|
continue;
|
|
|
|
/* A leaf or an internal node with skipped bits */
|
|
if (IS_LEAF(node) || ((struct tnode *)node)->pos > tn->pos + tn->bits) {
|
|
if (tkey_extract_bits(node->key, oldtnode->pos + oldtnode->bits, 1) == 0)
|
|
put_child(tn, 2*i, node);
|
|
else
|
|
put_child(tn, 2*i+1, node);
|
|
continue;
|
|
}
|
|
|
|
/* An internal (full) node with two children */
|
|
inode = (struct tnode *) node;
|
|
|
|
if (inode->bits == 1) {
|
|
put_child(tn, 2*i, inode->child[0]);
|
|
put_child(tn, 2*i+1, inode->child[1]);
|
|
tnode_free(inode);
|
|
continue;
|
|
}
|
|
|
|
/* An internal (full) node with more than two children */
|
|
left = (struct tnode *)tnode_get_child(tn, 2*i);
|
|
put_child(tn, 2*i, NULL); /* Temporarily remove */
|
|
|
|
right = (struct tnode *)tnode_get_child(tn, 2*i+1);
|
|
put_child(tn, 2*i+1, NULL); /* Temporarily remove */
|
|
|
|
size = tnode_child_length(left);
|
|
for (j = 0; j < size; j++) {
|
|
put_child(left, j, inode->child[j]);
|
|
put_child(right, j, inode->child[j + size]);
|
|
}
|
|
|
|
put_child(tn, 2*i, resize(left)); /* Restore */
|
|
put_child(tn, 2*i+1, resize(right)); /* Restore */
|
|
|
|
tnode_free(inode);
|
|
}
|
|
tnode_free(oldtnode);
|
|
return tn;
|
|
}
|
|
|
|
static struct tnode *halve(struct tnode *tn)
|
|
{
|
|
struct tnode *oldtnode = tn;
|
|
struct tentry *left, *right;
|
|
int i;
|
|
int olen = tnode_child_length(tn);
|
|
|
|
tn = tnode_new(oldtnode->key, oldtnode->pos, oldtnode->bits - 1);
|
|
|
|
if (!tn)
|
|
return NULL;
|
|
|
|
/* Create new internal tnode if necessary, with its children left empty */
|
|
for (i = 0; i < olen; i += 2) {
|
|
left = tnode_get_child(oldtnode, i);
|
|
right = tnode_get_child(oldtnode, i+1);
|
|
|
|
/* Two nonempty children */
|
|
if (left && right) {
|
|
struct tnode *newn;
|
|
/* Create a *full* tnode */
|
|
newn = tnode_new(left->key, tn->pos + tn->bits, 1);
|
|
if (!newn) {
|
|
tnode_clean_free(tn);
|
|
return NULL;
|
|
}
|
|
put_child(tn, i/2, (struct tentry *)newn);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < olen; i += 2) {
|
|
struct tnode *newBinNode;
|
|
|
|
left = tnode_get_child(oldtnode, i);
|
|
right = tnode_get_child(oldtnode, i+1);
|
|
|
|
/* At least one of the children is empty */
|
|
if (left == NULL) {
|
|
if (right == NULL) /* Both are empty */
|
|
continue;
|
|
put_child(tn, i/2, right);
|
|
continue;
|
|
}
|
|
|
|
if (right == NULL) {
|
|
put_child(tn, i/2, left);
|
|
continue;
|
|
}
|
|
|
|
/* Two nonempty children */
|
|
newBinNode = (struct tnode *)tnode_get_child(tn, i/2);
|
|
put_child(tn, i/2, NULL); /* Temporarily remove */
|
|
put_child(newBinNode, 0, left);
|
|
put_child(newBinNode, 1, right);
|
|
put_child(tn, i/2, resize(newBinNode)); /* Restore */
|
|
}
|
|
tnode_free(oldtnode);
|
|
return tn;
|
|
}
|
|
|
|
#define MAX_WORK 10
|
|
static struct tentry *resize(struct tnode *tn)
|
|
{
|
|
int i;
|
|
struct tnode *old_tn;
|
|
int inflate_threshold_use;
|
|
int halve_threshold_use;
|
|
int max_work;
|
|
|
|
if (!tn)
|
|
return NULL;
|
|
|
|
/* No children */
|
|
if (tn->empty_children == tnode_child_length(tn)) {
|
|
tnode_free(tn);
|
|
return NULL;
|
|
}
|
|
/* One child */
|
|
if (tn->empty_children == tnode_child_length(tn) - 1)
|
|
goto one_child;
|
|
|
|
/*
|
|
* Double as long as the resulting node has a number of
|
|
* nonempty nodes that are above the threshold.
|
|
*/
|
|
|
|
/* Keep root node larger */
|
|
if (!node_parent((struct tentry *)tn)) {
|
|
inflate_threshold_use = inflate_threshold_root;
|
|
halve_threshold_use = halve_threshold_root;
|
|
} else {
|
|
inflate_threshold_use = inflate_threshold;
|
|
halve_threshold_use = halve_threshold;
|
|
}
|
|
|
|
max_work = MAX_WORK;
|
|
while ((tn->full_children > 0 && max_work-- &&
|
|
50 * (tn->full_children + tnode_child_length(tn) - tn->empty_children)
|
|
>= inflate_threshold_use * tnode_child_length(tn))) {
|
|
|
|
old_tn = tn;
|
|
tn = inflate(tn);
|
|
|
|
if (!tn) {
|
|
tn = old_tn;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Return if at least one inflate is run */
|
|
if (max_work != MAX_WORK)
|
|
return (struct tentry *)tn;
|
|
|
|
/*
|
|
* Halve as long as the number of empty children in this
|
|
* node is above threshold.
|
|
*/
|
|
|
|
max_work = MAX_WORK;
|
|
while (tn->bits > 1 && max_work-- &&
|
|
100 * (tnode_child_length(tn) - tn->empty_children) <
|
|
halve_threshold_use * tnode_child_length(tn)) {
|
|
|
|
old_tn = tn;
|
|
tn = halve(tn);
|
|
|
|
if (!tn) {
|
|
tn = old_tn;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Only one child remains */
|
|
if (tn->empty_children == tnode_child_length(tn) - 1) {
|
|
one_child:
|
|
for (i = 0; i < tnode_child_length(tn); i++) {
|
|
struct tentry *n;
|
|
n = tn->child[i];
|
|
if (!n)
|
|
continue;
|
|
|
|
/* compress one level */
|
|
node_set_parent(n, NULL);
|
|
tnode_free(tn);
|
|
return n;
|
|
}
|
|
}
|
|
return (struct tentry *)tn;
|
|
}
|
|
|
|
static void trie_rebalance(struct tnode *tn)
|
|
{
|
|
int wasfull;
|
|
t_key cindex, key;
|
|
struct tnode *tp;
|
|
|
|
key = tn->key;
|
|
|
|
while (tn != NULL && (tp = node_parent((struct tentry *)tn)) != NULL) {
|
|
cindex = tkey_extract_bits(key, tp->pos, tp->bits);
|
|
wasfull = tnode_full(tp, tnode_get_child(tp, cindex));
|
|
|
|
tn = (struct tnode *)resize((struct tnode *)tn);
|
|
|
|
tnode_put_child_reorg((struct tnode *)tp, cindex, (struct tentry *)tn, wasfull);
|
|
|
|
tp = node_parent((struct tentry *) tn);
|
|
if (!tp)
|
|
trie = (struct tentry *)tn;
|
|
|
|
if (!tp)
|
|
break;
|
|
tn = tp;
|
|
}
|
|
|
|
/* Handle last (top) tnode */
|
|
if (IS_TNODE(tn))
|
|
tn = (struct tnode *)resize((struct tnode *)tn);
|
|
|
|
trie = (struct tentry *)tn;
|
|
}
|
|
|
|
static struct tleaf_info *find_leaf_info(struct tleaf *l, int plen)
|
|
{
|
|
struct tleaf_info *p;
|
|
|
|
if (!l)
|
|
return NULL;
|
|
|
|
hlist_for_each_entry(p, &l->head, node) {
|
|
if (p->plen == plen)
|
|
return p;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void insert_leaf_info(struct tleaf *l, struct tleaf_info *li)
|
|
{
|
|
struct tleaf_info *p = NULL;
|
|
struct tleaf_info *last = NULL;
|
|
struct hlist_head *head = &l->head;
|
|
|
|
if (hlist_empty(head)) {
|
|
hlist_add_head(&li->node, head);
|
|
} else {
|
|
hlist_for_each_entry(p, head, node) {
|
|
if (li->plen > p->plen)
|
|
break;
|
|
|
|
last = p;
|
|
}
|
|
if (last)
|
|
hlist_add_after(&last->node, &li->node);
|
|
else
|
|
hlist_add_before(&li->node, &p->node);
|
|
}
|
|
}
|
|
|
|
static struct tleaf *fib_find_node(unsigned int key)
|
|
{
|
|
int pos;
|
|
struct tnode *tn;
|
|
struct tentry *n;
|
|
|
|
pos = 0;
|
|
n = trie;
|
|
|
|
while (n != NULL && NODE_TYPE(n) == T_TNODE) {
|
|
tn = (struct tnode *) n;
|
|
if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) {
|
|
pos = tn->pos + tn->bits;
|
|
n = tnode_get_child(tn, tkey_extract_bits(key, tn->pos, tn->bits));
|
|
} else
|
|
break;
|
|
}
|
|
|
|
/* Case we have found a leaf. Compare prefixes */
|
|
if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key))
|
|
return (struct tleaf *)n;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int check_leaf(struct tleaf *l, t_key key, struct in6_addr *prefix6, int *plen4, int *plen6, u16 *ratio, u16 *adjacent, u8 *fmt, u8 *transpt)
|
|
{
|
|
struct tleaf_info *li;
|
|
struct hlist_head *head = &l->head;
|
|
hlist_for_each_entry(li, head, node) {
|
|
if (l->key == (key & li->mask_plen)) {
|
|
*prefix6 = li->prefix6;
|
|
if (plen4)
|
|
*plen4 = li->plen;
|
|
if (plen6)
|
|
*plen6 = li->prefix6_len;
|
|
if (ratio)
|
|
*ratio = li->ratio;
|
|
if (adjacent)
|
|
*adjacent = li->adjacent;
|
|
if (fmt)
|
|
*fmt = li->format;
|
|
if (transpt)
|
|
*transpt = li->transport;
|
|
#ifdef IVI_DEBUG_RULE
|
|
printk(KERN_DEBUG "ivi_rule_lookup: " NIP4_FMT "/%d -> " NIP6_FMT "/%d, ratio = %d, adjacent = %d, addr-format %d, transport %d\n",
|
|
NIP4(key), li->plen, NIP6(li->prefix6), li->prefix6_len, li->ratio, li->adjacent, li->format, li->transport);
|
|
#endif
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int ivi_rule_lookup(u32 key, struct in6_addr *prefix6, int *plen4, int *plen6, u16 *ratio, u16 *adjacent, u8 *fmt, u8 *transpt)
|
|
{
|
|
int ret;
|
|
struct tentry *n;
|
|
struct tnode *pn;
|
|
unsigned int pos, bits;
|
|
unsigned int chopped_off;
|
|
t_key cindex = 0;
|
|
unsigned int current_prefix_length = KEYLENGTH;
|
|
struct tnode *cn;
|
|
t_key pref_mismatch;
|
|
|
|
spin_lock_bh(&trie_lock);
|
|
|
|
n = trie;
|
|
if (!n)
|
|
goto failed;
|
|
|
|
/* Just a leaf? */
|
|
if (IS_LEAF(n)) {
|
|
ret = check_leaf((struct tleaf *)n, key, prefix6, plen4, plen6, ratio, adjacent, fmt, transpt);
|
|
goto found;
|
|
}
|
|
|
|
pn = (struct tnode *)n;
|
|
chopped_off = 0;
|
|
|
|
while (pn) {
|
|
pos = pn->pos;
|
|
bits = pn->bits;
|
|
|
|
if (!chopped_off)
|
|
cindex = tkey_extract_bits(mask_pfx(key, current_prefix_length), pos, bits);
|
|
|
|
n = tnode_get_child(pn, cindex);
|
|
|
|
if (n == NULL) {
|
|
goto backtrace;
|
|
}
|
|
|
|
if (IS_LEAF(n)) {
|
|
ret = check_leaf((struct tleaf *)n, key, prefix6, plen4, plen6, ratio, adjacent, fmt, transpt);
|
|
if (ret > 0)
|
|
goto backtrace;
|
|
goto found;
|
|
}
|
|
|
|
cn = (struct tnode *)n;
|
|
|
|
if (current_prefix_length < pos + bits) {
|
|
if (tkey_extract_bits(cn->key, current_prefix_length,
|
|
cn->pos - current_prefix_length)
|
|
|| !(cn->child[0]))
|
|
goto backtrace;
|
|
}
|
|
|
|
pref_mismatch = mask_pfx(cn->key ^ key, cn->pos);
|
|
|
|
if (pref_mismatch) {
|
|
int mp = KEYLENGTH - fls(pref_mismatch);
|
|
|
|
if (tkey_extract_bits(cn->key, mp, cn->pos - mp) != 0)
|
|
goto backtrace;
|
|
|
|
if (current_prefix_length >= cn->pos)
|
|
current_prefix_length = mp;
|
|
}
|
|
|
|
pn = (struct tnode *)n; /* Descend */
|
|
chopped_off = 0;
|
|
continue;
|
|
|
|
backtrace:
|
|
chopped_off++;
|
|
|
|
/* As zero don't change the child key (cindex) */
|
|
while ((chopped_off <= pn->bits)
|
|
&& !(cindex & (1<<(chopped_off-1))))
|
|
chopped_off++;
|
|
|
|
/* Decrease current_... with bits chopped off */
|
|
if (current_prefix_length > pn->pos + pn->bits - chopped_off)
|
|
current_prefix_length = pn->pos + pn->bits - chopped_off;
|
|
|
|
/*
|
|
* Either we do the actual chop off according or if we have
|
|
* chopped off all bits in this tnode walk up to our parent.
|
|
*/
|
|
if (chopped_off <= pn->bits) {
|
|
cindex &= ~(1 << (chopped_off-1));
|
|
} else {
|
|
struct tnode *parent = node_parent((struct tentry *) pn);
|
|
if (!parent)
|
|
goto failed;
|
|
|
|
/* Get Child's index */
|
|
cindex = tkey_extract_bits(pn->key, parent->pos, parent->bits);
|
|
pn = parent;
|
|
chopped_off = 0;
|
|
goto backtrace;
|
|
}
|
|
}
|
|
failed:
|
|
ret = 1;
|
|
found:
|
|
spin_unlock_bh(&trie_lock);
|
|
return ret;
|
|
}
|
|
|
|
static struct tleaf_info* trie_insert_node(u32 key, u32 plen)
|
|
{
|
|
int pos, newpos;
|
|
int missbit;
|
|
struct tleaf *l;
|
|
struct tleaf_info *li = NULL;
|
|
struct tentry *n;
|
|
struct tnode *tp = NULL, *tn = NULL;
|
|
t_key cindex;
|
|
|
|
pos = 0;
|
|
n = (struct tentry *)trie;
|
|
|
|
while (n != NULL && NODE_TYPE(n) == T_TNODE) {
|
|
tn = (struct tnode *)n;
|
|
if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) {
|
|
tp = tn;
|
|
pos = tn->pos + tn->bits;
|
|
n = tnode_get_child(tn, tkey_extract_bits(key, tn->pos, tn->bits));
|
|
} else
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* n ----> NULL, LEAF or TNODE
|
|
*
|
|
* tp is n's (parent) ----> NULL or TNODE
|
|
*/
|
|
|
|
/* Case 1: n is a leaf. Compare prefixes */
|
|
if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key)) {
|
|
l = (struct tleaf *)n;
|
|
li = tleaf_info_new(plen);
|
|
if (!li)
|
|
return NULL;
|
|
|
|
insert_leaf_info(l, li);
|
|
return li;
|
|
}
|
|
l = tleaf_new();
|
|
|
|
if (!l)
|
|
return NULL;
|
|
|
|
l->key = key;
|
|
li = tleaf_info_new(plen);
|
|
|
|
if (!li) {
|
|
tleaf_free(l);
|
|
return NULL;
|
|
}
|
|
|
|
insert_leaf_info(l, li);
|
|
|
|
if (trie != NULL && n == NULL) {
|
|
/* Case 2: n is NULL while we have root, just insert a new leaf */
|
|
node_set_parent((struct tentry *)l, tp);
|
|
cindex = tkey_extract_bits(key, tp->pos, tp->bits);
|
|
put_child((struct tnode *)tp, cindex, (struct tentry *)l);
|
|
} else {
|
|
/* Case 3: n is a LEAF or a TNODE and the key doesn't match. */
|
|
/*
|
|
* Add a new tnode here
|
|
* If root is NULL, first tnode need some special handling
|
|
*/
|
|
if (tp)
|
|
pos = tp->pos + tp->bits;
|
|
else
|
|
pos = 0;
|
|
|
|
if (n) {
|
|
newpos = tkey_mismatch(key, pos, n->key);
|
|
tn = tnode_new(n->key, newpos, 1);
|
|
} else {
|
|
newpos = 0;
|
|
tn = tnode_new(key, newpos, 1); /* First tnode (root) */
|
|
}
|
|
|
|
if (tn == NULL)
|
|
return NULL;
|
|
|
|
node_set_parent((struct tentry *)tn, tp);
|
|
|
|
missbit = tkey_extract_bits(key, newpos, 1);
|
|
put_child(tn, missbit, (struct tentry *)l);
|
|
put_child(tn, 1 - missbit, n);
|
|
|
|
if (tp) {
|
|
cindex = tkey_extract_bits(key, tp->pos, tp->bits);
|
|
put_child((struct tnode *)tp, cindex, (struct tentry *)tn);
|
|
} else {
|
|
trie = (struct tentry *)tn;
|
|
tp = tn;
|
|
}
|
|
}
|
|
/* Re-balance the trie */
|
|
trie_rebalance(tp);
|
|
return li;
|
|
}
|
|
|
|
int ivi_rule_insert(struct rule_info *rule)
|
|
{
|
|
u32 key, mask;
|
|
int plen;
|
|
struct tleaf *l;
|
|
struct tleaf_info *li;
|
|
|
|
if ((rule->plen4 > 32) || (rule->plen6 > 128))
|
|
return -1;
|
|
|
|
plen = rule->plen4;
|
|
mask = ntohl(inet_make_mask(plen));
|
|
key = rule->prefix4 & mask;
|
|
|
|
spin_lock_bh(&trie_lock);
|
|
l = fib_find_node(key);
|
|
li = find_leaf_info(l, plen);
|
|
if (li) {
|
|
// Update satellite data.
|
|
li->prefix6 = rule->prefix6;
|
|
li->prefix6_len = rule->plen6;
|
|
li->ratio = rule->ratio;
|
|
li->adjacent = rule->adjacent;
|
|
li->format = rule->format;
|
|
li->transport = rule->transport;
|
|
} else {
|
|
li = trie_insert_node(key, plen);
|
|
// Insert satellite data.
|
|
li->prefix6 = rule->prefix6;
|
|
li->prefix6_len = rule->plen6;
|
|
li->ratio = rule->ratio;
|
|
li->adjacent = rule->adjacent;
|
|
li->format = rule->format;
|
|
li->transport = rule->transport;
|
|
}
|
|
spin_unlock_bh(&trie_lock);
|
|
#ifdef IVI_DEBUG_RULE
|
|
printk(KERN_DEBUG "ivi_rule_insert: " NIP4_FMT "/%d -> " NIP6_FMT "/%d, ratio %d, adjacent %d, addr-format %d, transport %d\n",
|
|
NIP4(rule->prefix4), rule->plen4, NIP6(rule->prefix6), rule->plen6, rule->ratio, rule->adjacent, rule->format, rule->transport);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void trie_leaf_remove(struct tleaf *l)
|
|
{
|
|
struct tnode *tp = node_parent((struct tentry *)l);
|
|
|
|
if (tp) {
|
|
t_key cindex = tkey_extract_bits(l->key, tp->pos, tp->bits);
|
|
put_child((struct tnode *)tp, cindex, NULL);
|
|
trie_rebalance(tp);
|
|
} else
|
|
trie = NULL;
|
|
|
|
tleaf_free(l);
|
|
}
|
|
|
|
int ivi_rule_delete(struct rule_info *rule)
|
|
{
|
|
u32 key, mask;
|
|
int plen, ret;
|
|
struct tleaf *l;
|
|
struct tleaf_info *li;
|
|
|
|
ret = -1;
|
|
|
|
key = rule->prefix4;
|
|
plen = rule->plen4;
|
|
|
|
if (plen > 32)
|
|
goto out;
|
|
|
|
mask = ntohl(inet_make_mask(plen));
|
|
key = key & mask;
|
|
|
|
spin_lock_bh(&trie_lock);
|
|
l = fib_find_node(key);
|
|
if (!l) {
|
|
goto out_from_lock;
|
|
}
|
|
li = find_leaf_info(l, plen);
|
|
if (!li)
|
|
goto out_from_lock;
|
|
|
|
/* Here we need to check whether 'li' matches the provided 'rule'
|
|
* since no check against *prefix6* is performed before.
|
|
*/
|
|
if (ipv6_addr_cmp(&li->prefix6, &rule->prefix6) || li->prefix6_len != rule->plen6
|
|
|| li->format != rule->format || li->ratio != rule->ratio || li->adjacent != rule->adjacent || li->transport != rule->transport)
|
|
goto out_from_lock;
|
|
|
|
hlist_del(&li->node);
|
|
tleaf_info_free(li);
|
|
#ifdef IVI_DEBUG_RULE
|
|
printk(KERN_DEBUG "ivi_rule_delete: " NIP4_FMT "/%d -> " NIP6_FMT "/%d, ratio = %d, adjacent = %d, addr-format %d, transport %d\n",
|
|
NIP4(rule->prefix4), rule->plen4, NIP6(rule->prefix6), rule->plen6, rule->ratio, rule->adjacent, rule->format, rule->transport);
|
|
#endif
|
|
|
|
if (hlist_empty(&l->head))
|
|
trie_leaf_remove(l);
|
|
|
|
ret = 0;
|
|
out_from_lock:
|
|
spin_unlock_bh(&trie_lock);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Scan for the next right_leaf starting at node c
|
|
*/
|
|
static struct tleaf *leaf_walk(struct tnode *p, struct tentry *c)
|
|
{
|
|
do {
|
|
t_key idx;
|
|
|
|
if (c)
|
|
idx = tkey_extract_bits(c->key, p->pos, p->bits) + 1;
|
|
else
|
|
idx = 0;
|
|
|
|
while (idx < 1u << p->bits) {
|
|
c = tnode_get_child(p, idx++);
|
|
if (!c)
|
|
continue;
|
|
|
|
if (IS_LEAF(c)) {
|
|
return (struct tleaf *)c;
|
|
}
|
|
|
|
/* Descend and start scanning in new node */
|
|
p = (struct tnode *)c;
|
|
idx = 0;
|
|
}
|
|
|
|
/* Node empty, walk back up to parent */
|
|
c = (struct tentry *)p;
|
|
} while ((p = node_parent(c)) != NULL);
|
|
|
|
return NULL; /* Root of trie */
|
|
}
|
|
|
|
static struct tleaf *trie_first_leaf(struct tentry *t)
|
|
{
|
|
|
|
struct tnode *n = (struct tnode *)t;
|
|
|
|
if (!n)
|
|
return NULL;
|
|
|
|
if (IS_LEAF(n)) /* trie is just a leaf */
|
|
return (struct tleaf *)n;
|
|
|
|
return leaf_walk(n, NULL);
|
|
}
|
|
|
|
static struct tleaf *trie_next_leaf(struct tleaf *l)
|
|
{
|
|
struct tentry *c = (struct tentry *)l;
|
|
struct tnode *p = node_parent(c);
|
|
|
|
if (!p)
|
|
return NULL; /* trie with just one leaf as its root */
|
|
|
|
return leaf_walk(p, c);
|
|
}
|
|
|
|
static void trie_flush_leaf(struct tleaf *l)
|
|
{
|
|
struct tleaf_info *li = NULL;
|
|
struct hlist_node *loop;
|
|
|
|
if (!l)
|
|
return;
|
|
|
|
hlist_for_each_entry_safe(li, loop, &l->head, node) {
|
|
hlist_del(&li->node);
|
|
tleaf_info_free(li);
|
|
}
|
|
}
|
|
|
|
void ivi_rule_flush(void)
|
|
{
|
|
struct tleaf *l, *ll = NULL;
|
|
|
|
spin_lock_bh(&trie_lock);
|
|
|
|
for (l = trie_first_leaf(trie); l; l = trie_next_leaf(l)) {
|
|
trie_flush_leaf(l);
|
|
|
|
if (ll && hlist_empty(&ll->head))
|
|
trie_leaf_remove(ll);
|
|
ll = l;
|
|
}
|
|
|
|
if (ll && hlist_empty(&ll->head))
|
|
trie_leaf_remove(ll);
|
|
|
|
spin_unlock_bh(&trie_lock);
|
|
}
|
|
|
|
int ivi_rule_init(void) {
|
|
trie = NULL;
|
|
spin_lock_init(&trie_lock);
|
|
#ifdef IVI_DEBUG
|
|
balance = 0;
|
|
printk(KERN_DEBUG "IVI: ivi_rule loaded.\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void ivi_rule_exit(void) {
|
|
ivi_rule_flush();
|
|
#ifdef IVI_DEBUG
|
|
printk(KERN_DEBUG "IVI: ivi_rule unloaded.\n");
|
|
printk(KERN_DEBUG "IVI: ivi_rule memory balance = %d\n", balance);
|
|
#endif
|
|
}
|