上面几节分析了路由的添加与查找,本节开始分析路由的删除。
分析了这么多linuxkernel功能模块,对于功能模块的大致实现流程,我们也比较熟悉了,无非是状态机、数据结构(以及数据之间的关系,通过链表等实现)、垃圾处理等。但是对于ipv4来说,既有路由项相关的数据结构,也有路由缓存相关的数据结构,通过这两个数据结构的名称,我们也可以看出来,路由项应该是没有垃圾回收机制的,不然的话路由缓存应该就没有存在的必要了吧。因此对于路由项的删除,一般是应用层通过命令删除的,kernel不会自动删除路由项的。
对于应用层的删除命令,就是通过socket来实现的。例如通过SIOCDELRT删除路由项,应用层的命令基本上都是通过调用ip_rt_ioctl、inet_rtm_delroute来实现的。
1.路由删除的步骤
在分析相应的函数之前,我们先来分析一下,删除一个路由项需要哪些步骤呢?
a通过输入参数,找到相应的路由表
b在相应的路由表,找到相应的路由项
c删除该路由项,且需要将该路由项上相对应的路由缓存删除掉
下面我们就开始分析删除实现的流程。在函数ip_rt_ioctl、inet_rtm_delroute均会先调用函数fib_get_table找到对应的路由表,接着就调用路由表对应的删除函数,删除对应的路由项,即tb->tb_delete。对于ipv4即为函数fn_hash_delete
1.1fn_hash_delete
下面分析这个函数,这个函数即从路由表中删除一个路由项。
1.根据网络掩码长度获取相应的fn_zone变量,
若该变量为NULL,则程序返回错误;
若该变量不为0,则执行2。
2.对目的地址进行合法性检测,并调用函数fz_key构造查找关键字key
3.根据搜索关键字key与fn_zone,调用fib_find_node查找符合条件的fib_node
a)若没有查找到符合条件的fib_node结构变量,则程序返回错误
b)若查找到了,则执行4
4.调用函数fib_find_alias查找符合条件的fib_alias
a)若没有查找到,则程序返回错误
b)若查找到则执行5
5.从查找到的fib_alias处开始遍历余下所有的fib_alias,精确匹配要删除的路由项
查找到以后:
a)将该fib_alias从fib_node->fn_alias链表中删除
b)若从fib_node->fn_alias链表中删除当前fib_alias后,fib_node->fn_alias为空,则同时需要从fn_zone->fz_hash链表数组中删除该fib_node节点;若fib_node->fn_alias不为空,则执行
c)若该fib_alias在路由查找中,被查找匹配过,则需要调用函数rt_cache_flush刷新路由缓存
d)调用函数fn_free_alias释放该fib_alias变量占用的缓存,并调用函数fib_release_info准备释放该fib_alias变量包含的fib_info变量
e)在函数fib_release_info里,会结合fib_treeref与fib_prefsrc的值来决定是否释放fib_info占用的内存
staticintfn_hash_delete(structfib_table*tb,structfib_config*cfg)
{
structfn_hash*table=(structfn_hash*)tb->tb_data;
structfib_node*f;
structfib_alias*fa,*fa_to_delete;
structfn_zone*fz;
__be32key;
/*判断掩码的长度是否有效*/
if(cfg->fc_dst_len>32)
return-EINVAL;
if((fz=table->fn_zones[cfg->fc_dst_len])==NULL)
return-ESRCH;
key=0;
if(cfg->fc_dst){
if(cfg->fc_dst&~FZ_MASK(fz))
return-EINVAL;
key=fz_key(cfg->fc_dst,fz);
}
f=fib_find_node(fz,key);
if(!f)
fa=NULL;
else
fa=fib_find_alias(&f->fn_alias,cfg->fc_tos,0);
if(!fa)
return-ESRCH;
fa_to_delete=NULL;
fa=list_entry(fa->fa_list.prev,structfib_alias,fa_list);
list_for_each_entry_continue(fa,&f->fn_alias,fa_list){
structfib_info*fi=fa->fa_info;
if(fa->fa_tos!=cfg->fc_tos)
break;
if((!cfg->fc_type||
fa->fa_type==cfg->fc_type)&&
(cfg->fc_scope==RT_SCOPE_NOWHERE||
fa->fa_scope==cfg->fc_scope)&&
(!cfg->fc_protocol||
fi->fib_protocol==cfg->fc_protocol)&&
fib_nh_match(cfg,fi)==0){
fa_to_delete=fa;
break;
}
}
if(fa_to_delete){
intkill_fn;
fa=fa_to_delete;
rtmsg_fib(RTM_DELROUTE,key,fa,cfg->fc_dst_len,
tb->tb_id,&cfg->fc_nlinfo);
kill_fn=0;
write_lock_bh(&fib_hash_lock);
list_del(&fa->fa_list);
if(list_empty(&f->fn_alias)){
hlist_del(&f->fn_hash);
kill_fn=1;
}
fib_hash_genid++;
write_unlock_bh(&fib_hash_lock);
/*若fib_alias->fa_state的FA_S_ACCESSED位为1,则说明该fib_alias被访问过,
因此需要调用函数rt_cache_flush,刷新路由缓存,以便删除该路
由项对应的路由缓存*/
if(fa->fa_state&FA_S_ACCESSED)
rt_cache_flush(-1);
fn_free_alias(fa);
if(kill_fn){
fn_free_node(f);
fz->fz_nent–;
}
return0;
}
return-ESRCH;
}
这个函数调用了几个查找函数,下面分析下这几个函数
1.2fib_find_node
功能:根据搜索关键字,在fn_zone变量fz中查找符合条件的fib_node变量
1.调用fn_hash,根据搜索关键字与fn_zone变量计算hash值为hash_index
2.根据hash值hash_index获取到hash链表的头部
3.调用函数hlist_for_each_entry遍历该链表,查找fib_node->fn_key等于传入的fn_zone,
若查找到则返回该fib_node变量;若没有找到返回NULL
staticstructfib_node*fib_find_node(structfn_zone*fz,__be32key)
{
structhlist_head*head=&fz->fz_hash[fn_hash(key,fz)];
structhlist_node*node;
structfib_node*f;
hlist_for_each_entry(f,node,head,fn_hash){
if(f->fn_key==key)
returnf;
}
returnNULL;
}
1.3fib_find_alias
功能:根据tos、priority查找符匹配的fib_alias变量
1.遍历链表fah,查找tos小于传递的tos,且fib_priority大于或等于传递的prio的fib_alias变量
structfib_alias*fib_find_alias(structlist_head*fah,u8tos,u32prio)
{
if(fah){
structfib_alias*fa;
list_for_each_entry(fa,fah,fa_list){
if(fa->fa_tos>tos)
continue;
if(fa->fa_info->fib_priority>=prio||
fa->fa_tos<tos)
returnfa;
}
}
returnNULL;
}
1.4fn_free_alias
功能:释放一个fib_alias结构的变量占用的缓存,并释放其关联的fib_info结构的变量
staticinlinevoidfn_free_alias(structfib_alias*fa)
{
fib_release_info(fa->fa_info);
kmem_cache_free(fn_alias_kmem,fa);
}
1.5fn_free_node
功能:释放一个fib_node结构的变量占用的缓存
staticinlinevoidfn_free_node(structfib_node*f)
{
kmem_cache_free(fn_hash_kmem,f);
}
以上就是路由删除的主要分析流程,路由删除相关的流程函数比较简单的,只要理解了路由项相关的数据结构,基本上对于路由删除的操作,就一目了然了。
后面准备先分析策略规则相关的流程,分析策略规则后,则开始分析路由缓存模块,等到这些功能模块都分析完了以后,对于kernel协议栈的分析也就算告一段落了。
而是他们在同伴们都睡着的时候,一步步艰辛地