2.2.4 路由删除
应用层通过SIOCDELRT删除路由项,一般调用ip_rt_ioctl或者inet_rtm_delroute。
这里我们通过inet_rtm_delroute来学习。
接下来,学习fn_hash_delete函数
功能:
1)根据目的地址,找对应的fn_zone->fib_node->fib_alias
2)遍历fib_alias链表找到精确要删除的路由表项
3)删除并释放fib_alias->fib_node
4)刷新路由缓存,通知用户空间。
应用层通过SIOCDELRT删除路由项,一般调用ip_rt_ioctl或者inet_rtm_delroute。
这里我们通过inet_rtm_delroute来学习。
static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct net *net = sock_net(skb->sk);
struct fib_config cfg;
struct fib_table *tb;
int err;
//将netlink传递的用户空间信息输出到config中
err = rtm_to_fib_config(net, skb, nlh, &cfg);
if (err < 0)
goto errout;
tb = fib_get_table(net, cfg.fc_table);//找到要对应的路由表
if (tb == NULL) {
err = -ESRCH;
goto errout;
}
err = tb->tb_delete(tb, &cfg);//调用fn_hash_delete删除表项
errout:
return err;
}
2.2.4.1路由表项删除
接下来,学习fn_hash_delete函数
功能:
1)根据目的地址,找对应的fn_zone->fib_node->fib_alias
2)遍历fib_alias链表找到精确要删除的路由表项
3)删除并释放fib_alias->fib_node
4)刷新路由缓存,通知用户空间。
程序流程图:
static int fn_hash_delete(struct fib_table *tb, struct fib_config *cfg)
{
struct fn_hash *table = (struct fn_hash *)tb->tb_data;
struct fib_node *f;
struct fib_alias *fa, *fa_to_delete;
struct fn_zone *fz;
__be32 key;
if (cfg->fc_dst_len > 32)
return -EINVAL;
//根据目的地址长度得到fn_zone
if ((fz = table->fn_zones[cfg->fc_dst_len]) == NULL)
{
DEBUG_V4Route("%s-->can't find fn_zone dst_len:%d\n",__FUNCTION__,cfg->fc_dst_len);
return -ESRCH;
}
key = 0;
if (cfg->fc_dst) {
if (cfg->fc_dst & ~FZ_MASK(fz))
return -EINVAL;
key = fz_key(cfg->fc_dst, fz);//根据目的地址和fn_zone获得key值
}
f = fib_find_node(fz, key);//根据key和fn_zone找到fib_node
if (!f)
fa = NULL;
else
fa = fib_find_alias(&f->fn_alias, cfg->fc_tos, 0);//根据tos、优先级找到fib_alias
if (!fa)
{
DEBUG_V4Route("%s-->Can't find fib_alias fa_tos:%d\n",__FUNCTION__,cfg->fc_tos);
return -ESRCH;
}
fa_to_delete = NULL;//遍历余下的fib_alias,精确匹配要删除的路由表项
fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list);
list_for_each_entry_continue(fa, &f->fn_alias, fa_list) {
struct fib_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) {
DEBUG_V4Route("%s-->Find fib_alias to delete fa_type:%d fa_scope:%d fib_protocol:%d\n",__FUNCTION__,\
fa->fa_type,fa->fa_scope,fi->fib_protocol);
fa_to_delete = fa;
break;
}
}
if (fa_to_delete) {
int kill_fn;
fa = fa_to_delete;
kill_fn = 0;
write_lock_bh(&fib_hash_lock);
list_del(&fa->fa_list);//从链表中删除该fib_alias
if (list_empty(&f->fn_alias)) {//若fib_alias链表为空,则删除对应的fib_node节点
hlist_del(&f->fn_hash);
kill_fn = 1;
}
fib_hash_genid++;
write_unlock_bh(&fib_hash_lock);
if (fa->fa_state & FA_S_ACCESSED)//若该路由表项被访问过,则要刷新路由缓存
rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
fn_free_alias(fa, f);//释放该fib_alias
if (kill_fn) {
fn_free_node(f);//若fib_alias链表为空,释放对应的fib_node
fz->fz_nent--;
}
//通知用户空间
rtmsg_fib(RTM_DELROUTE, key, fa, cfg->fc_dst_len,
tb->tb_id, &cfg->fc_nlinfo, 0);
return 0;
}
DEBUG_V4Route("%s-->Cant't find fib_alias\n",__FUNCTION__);
return -ESRCH;
}