key_vector是linux网络路由时,非常重要的一个结构,其定义如下:
struct key_vector {
t_key key;
unsigned char pos; /* 2log(KEYLENGTH) bits needed */
unsigned char bits; /* 2log(KEYLENGTH) bits needed */
unsigned char slen; 子网长度
union {
/* This list pointer if valid if (pos | bits) == 0 (LEAF) */
struct hlist_head leaf;
/* This array is valid if (pos | bits) > 0 (TNODE) */
struct key_vector __rcu *tnode[0];
};
};
先不解释结构中几个字段的含义,首先用一段代码,将系统中顶层的key_vector,及其下层的key_vector中的数据打印出来。
void PrintKeyVector(struct key_vector* p_kv)
{
int n_count;
int i;
if (NULL == p_kv)
{
return;
}
printk("----------\n");
printk("key: %lx\n", p_kv->key);
printk("pos: %x\n", p_kv->pos);
printk("bit: %x\n", p_kv->bits);
printk("slen: %x\n", p_kv->slen);
}
static int hello_open(struct inode* inode, struct file*filep)
{
struct task_struct *tsk = current;
struct fib_table *tb;
struct trie *t;
struct key_vector *n, *pn;
int i = 0;
int n_count;
// 主表-254 RT_TABLE_MAIN
// 本地表-255 RT_TABLE_LOCAL
tb = fib_get_table(tsk->nsproxy->net_ns, RT_TABLE_MAIN);
printk("fib_table tb_id: %d\n", tb->tb_id);
printk("fib_table tb_num_default: %d\n", tb->tb_num_default);
t = (struct trie *) tb->tb_data;
pn = t->kv;
n = get_child_rcu(pn, 0);
PrintKeyVector(n);
n_count = 1 << n->bits;
for (; i < n_count; ++i)
{
struct key_vector* pkv = n->tnode[i];
printk("index = %d\n", i);
PrintKeyVector(pkv);
}
return 0;
}
打印的日志:
[ 227.486538] fib_table tb_id: 254
[ 227.486540] fib_table tb_num_default: 1
[ 227.486540] ----------
[ 227.486541] key: 0
[ 227.486542] pos: 1d
[ 227.486542] bit: 3
[ 227.486543] slen: 20
[ 227.486544] index = 0
[ 227.486544] ----------
[ 227.486545] key: 0
[ 227.486545] pos: 0
[ 227.486546] bit: 0
[ 227.486546] slen: 20
[ 227.486547] index = 1
[ 227.486548] index = 2
[ 227.486548] index = 3
[ 227.486549] ----------
[ 227.486549] key: 7f000000
[ 227.486550] pos: 16
[ 227.486550] bit: 2
[ 227.486551] slen: 18
[ 227.486551] index = 4
[ 227.486552] index = 5
[ 227.486552] ----------
[ 227.486553] key: a8000000
[ 227.486554] pos: 19
[ 227.486554] bit: 2
[ 227.486555] slen: 19
[ 227.486555] index = 6
[ 227.486556] ----------
[ 227.486556] key: c0a80000
[ 227.486557] pos: 6
[ 227.486557] bit: 2
[ 227.486558] slen: 8
[ 227.486559] index = 7
根据打印的日志,整理了下面的结构图:
图1
中间是上层的key_vector,两边的4个属于其下层。
1. key
key表示的是IP地址或IP地址段,如key=0,表示的地址是0.0.0.0;key=0xc0a80000,表示的地址是192.168.0.0。
2. bit
bit用来表示下层有几个key_vector,下层对象的指针,存放在tnode指向的数组中。用2的bit次方,可以计算出下层key_vector的数量。中间key_vector的bit为3,表示其下面有8个key_vector。
3. pos
pos需要和bit一块使用,从pos开始的bit个位,表示了下层vector在本层vector的tnode数组中存放的位置。
中间vector的pos为29,bit为3,说明下层key_vector的key,从29开始的3位(已标红的3位),表识了在中间key_vector数组的索引。
如:key为0xc0a80000(即192.168.0.0)的key_vector,对应的3位为0x110,该对象的指针,存放在中间key_vector数组的索引为6的位置。
4. slen
slen为后缀长度。
用上面同样的方法,将192.168.0.0的下层key_vector打印出来。
图2
上层pos为6,bits为2,因此下层key中从第6位开始的2位,标识了下层在上次数组中的索引。
#define get_cindex(key, kv) (((key) ^ (kv)->key) >> (kv)->pos)
get_cindex用于计算地址在tnode数组中的索引。
本机ip是192.168.0.106,下面分析下查找192.168.0.100路由的过程。
1层路由:
目的地址 addr = 0xc0a80064
key_vector对应图1中间那个,pos为29,key为0,调用get_cindex计算出索引为6,则次层为图1中左下角的key_vector。
2层路由
目的地址 addr = 0xc0a80064
key_vector对应图2中左上角那个,pos为6,key为0xc0a80000,调用get_cindex计算出索引为1,则再下一层为图2右下角那个,也是查找的目的节点。
理解了上面的内容,再看内核fib_table_lookup的代码时,就清晰多了。