看完fib的查找,弄清了一些数据结构的组织,我们再来看看路由表是如何创建的
从ip_fib_init注册的两个通知链来看,在IP地址发送变动时会触发通知链的处理函数,都会调用fib_add_ifaddr()来添加地址到路由中
这个里面由调用了fib_magic来进行路由地址的操作。
static void fib_magic(int cmd, int type, __be32 dst, int dst_len, struct in_ifaddr *ifa)
{
struct net *net = dev_net(ifa->ifa_dev->dev);
struct fib_table *tb;
struct fib_config cfg = { 路由配置结构
.fc_protocol = RTPROT_KERNEL,
.fc_type = type,
.fc_dst = dst,
.fc_dst_len = dst_len,
.fc_prefsrc = ifa->ifa_local,
.fc_oif = ifa->ifa_dev->dev->ifindex,
.fc_nlflags = NLM_F_CREATE | NLM_F_APPEND,
.fc_nlinfo = {
.nl_net = net,
},
};
if (type == RTN_UNICAST)
tb = fib_new_table(net, RT_TABLE_MAIN);
else
tb = fib_new_table(net, RT_TABLE_LOCAL);
if (tb == NULL)
return;
cfg.fc_table = tb->tb_id;
if (type != RTN_LOCAL)
cfg.fc_scope = RT_SCOPE_LINK;
else
cfg.fc_scope = RT_SCOPE_HOST;
if (cmd == RTM_NEWROUTE) 如果是添加命令 则插入
tb->tb_insert(tb, &cfg); fn_hash_insert;
else
tb->tb_delete(tb, &cfg); 否则,进行删除
}
{
struct net *net = dev_net(ifa->ifa_dev->dev);
struct fib_table *tb;
struct fib_config cfg = { 路由配置结构
.fc_protocol = RTPROT_KERNEL,
.fc_type = type,
.fc_dst = dst,
.fc_dst_len = dst_len,
.fc_prefsrc = ifa->ifa_local,
.fc_oif = ifa->ifa_dev->dev->ifindex,
.fc_nlflags = NLM_F_CREATE | NLM_F_APPEND,
.fc_nlinfo = {
.nl_net = net,
},
};
if (type == RTN_UNICAST)
tb = fib_new_table(net, RT_TABLE_MAIN);
else
tb = fib_new_table(net, RT_TABLE_LOCAL);
if (tb == NULL)
return;
cfg.fc_table = tb->tb_id;
if (type != RTN_LOCAL)
cfg.fc_scope = RT_SCOPE_LINK;
else
cfg.fc_scope = RT_SCOPE_HOST;
if (cmd == RTM_NEWROUTE) 如果是添加命令 则插入
tb->tb_insert(tb, &cfg); fn_hash_insert;
else
tb->tb_delete(tb, &cfg); 否则,进行删除
}
fib_new_table()
先看CONFIG_IP_MULTIPLE_TABLES开启的时候
struct fib_table *fib_new_table(struct net *net, u32 id)
{
struct fib_table *tb;
unsigned int h;
if (id == 0)
id = RT_TABLE_MAIN;
tb = fib_get_table(net, id); 通过id查找路由表函数
if (tb)
return tb;
tb = fib_hash_table(id); 申请创建路由表
if (!tb)
return NULL;
h = id & (FIB_TABLE_HASHSZ - 1);
hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]);
return tb;
}
{
struct fib_table *tb;
unsigned int h;
if (id == 0)
id = RT_TABLE_MAIN;
tb = fib_get_table(net, id); 通过id查找路由表函数
if (tb)
return tb;
tb = fib_hash_table(id); 申请创建路由表
if (!tb)
return NULL;
h = id & (FIB_TABLE_HASHSZ - 1);
hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]);
return tb;
}
struct fib_table *fib_get_table(struct net *net, u32 id)
{
struct fib_table *tb;
struct hlist_node *node;
struct hlist_head *head;
unsigned int h;
if (id == 0)
id = RT_TABLE_MAIN;
h = id & (FIB_TABLE_HASHSZ - 1);
rcu_read_lock();
head = &net->ipv4.fib_table_hash[h]; 得到冲突链头
hlist_for_each_entry_rcu(tb, node, head, tb_hlist) { 匹配
if (tb->tb_id == id) {
rcu_read_unlock();
return tb;
}
}
rcu_read_unlock();
return NULL;
}
当然FIB的东西还有很多很多,数据结构关系错综复杂,鉴于时间和精力,无法再深入分析,这里也只是弄清大概流程,拉通整个协议栈,等到有了系统的、全流程的观念后,我们再来仔细深入分析具体的模块。
这里有篇文章很好的阐述了linux kernel路由机制http://blog.youkuaiyun.com/bin323/article/details/642192 算是对上述的流程分析一个总结