fib_del_ifaddr函数的一个问题
最近在复习以前阅读的linux网络内核源码笔记,发现了一个以前忽略的问题,下面我列出我有疑问的代码872 static void fib_del_ifaddr(struct in_ifaddr *ifa)
873 {
...
942 if (!(ok&LOCAL_OK)) {
943 fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 32, prim);
944
945 /* Check, that this local address finally disappeared. */
946 if (inet_addr_type(ifa->ifa_local) != RTN_LOCAL) {
947 /* And the last, but not the least thing.
948 We must flush stray FIB entries.
949
950 First of all, we scan fib_info list searching
951 for stray nexthop entries, then ignite fib_flush.
952 */
953 if (fib_sync_down(ifa->ifa_local, NULL, 0))
954 fib_flush();
955 }
956 }
...
961 }
如上红色字体所示代码,通过inet_addr_type函数判断当前被删除的地址ifa对应的路由是否为RTN_LOCAL,如果不是则调用下面的fib_sync_down,我这里的问题是,什么样的地址具有这样的非RTN_LOCAL特征?作为对比,在fib_add_ifaddr函数中我们可以给linux添加一个ip,而该添加函数中我反复阅读了下,都找不到能在这里满足此红色字体语句的那样的ifa,这到底是怎么回事?以前阅读这段代码时没有想到这个细节,望各位能给予解答! 本帖最后由 jiufei19 于 2015-04-27 12:01 编辑
刚才我描述问题时,遗漏了一个相关内容,即inet_addr_type函数,如下
162 unsigned inet_addr_type(__be32 addr)
163 {
164 struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };
165 struct fib_result res;
166 unsigned ret = RTN_BROADCAST;
167
168 if (ZERONET(addr) || BADCLASS(addr))
169 return RTN_BROADCAST;
170 if (MULTICAST(addr))
171 return RTN_MULTICAST;
172
173 #ifdef CONFIG_IP_MULTIPLE_TABLES
174 res.r = NULL;
175 #endif
176
177 if (ip_fib_local_table) {
178 ret = RTN_UNICAST;
179 if (!ip_fib_local_table->tb_lookup(ip_fib_local_table,
180 &fl, &res)) {
181 ret = res.type;
182 fib_res_put(&res);
183 }
184 }
185 return ret;
186 }
显然,在inet_addr_type函数有上述兰色字体表示了该ifa有可能为非RTN_LOCAL的情况,但是我之前的疑问并非指这两种特殊情况,也即除了这2种情况外,还有其他非RTN_LOCAL的情况吗?
接着描述我的困惑:
假设这个被删除的ifa既不是multicast,也不是ZERONET或BADCLASS,那么inet_addr_type函数中从177行开始的判断到底是为什么而进行的呢?根据Understanding Linux Network Internals一书中所说,有一种特殊的地址添加情况,即:
# ip addr add dev eth0 192.168.0.1/24
# ip addr add dev eth0 192.168.0.1/16
此时同一个地址配置了两个掩码,于是当删除其中一个地址时,将不能简单就去掉到此地址的路由,因为还有另外一个地址和掩码存在着,但是这样的话,LOCAL_OK标记就不成立了,代码也就不会继续执行后面的inet_addr_type判断了。显然,当一个唯一性的local地址被删除后,才会引发后续的inet_addr_type的判断,而这个判断对于这种唯一性的地址似乎没有任何意义呢?
本帖最后由 jiufei19 于 2015-04-24 17:52 编辑
为了找到问题,作为对比,今天又把fib_add_ifaddr函数仔细看了一遍,发现仍然无法理解我这里提出的问题。
void fib_add_ifaddr(struct in_ifaddr *ifa)
{
...
fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);
...
}
这里显然被添加的ip对应的路由都是RTN_LOCAL,那么在fib_del_ifaddr函数中似乎永远也不会出现 if (inet_addr_type(ifa->ifa_local) != RTN_LOCAL) 的情况了(除了我前面的帖子中指明的那两个特殊的类型以外),这到底是怎么一回事呢?只能是我对地址和路由的概念有地方理解错了,请大家指正!
PS:
忽然想到一个问题,即inet_addr_type函数的调用点有好几个地方,或许是在fib_del_ifaddr这个调用场景下,被删除的地址只能是组播、badclass、zeronet三种形式! 本帖最后由 nswcfd 于 2015-04-24 19:50 编辑
没有那么复杂吧?
inet_addr_type(946行)前面的fib_magic(943)已经把local表中路由删除了,
那么inet_addr_type查找local表多半会失败的,失败就返回RTN_UNICAST,不就命中了这个分支?
========================================
以上纯属猜测,待会儿加上Printk测试一下。 本帖最后由 nswcfd 于 2015-04-24 20:22 编辑
通过printk观察,貌似是这样的。
正在努力思考问题的反方面:
什么情况下,在local表中删除了ifa_local对应的路由,而inet_addr_type仍然会返回RTN_LOCAL(即表中还有ifa_local对应的路由)? 一种可能性是人为的在local路由表中加入了匹配某接口IP的非32掩码的路由条目(非kernel自动生成的)…… 本帖最后由 jiufei19 于 2015-04-27 11:51 编辑
回复 4# nswcfd
感谢nswcfd的回答。对于nswcfd的如下说明:
-------------------------------------------------------------
inet_addr_type(946行)前面的fib_magic(943)已经把local表中路由删除了,
那么inet_addr_type查找local表多半会失败的,失败就返回RTN_UNICAST,不就命中了这个分支?
--------------------------------------------------------------
我的问题就是这里,这样做的原因到底是什么?因为似乎inet_addr_type的查找local表一定会失败的,多此一举的目的是什么呢? 本帖最后由 jiufei19 于 2015-04-27 11:48 编辑
回复 5# nswcfd
根据代码可知,当被删除的一个ip具有唯一性时,其一定是RTN_LOCAL类型,于是有:
-----------------------------------------------------------------------------------------
if (!(ok&LOCAL_OK)) {// 地址列表中已无此地址,可删除之
fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 32,
prim);
if (inet_addr_type(ifa->ifa_local) != RTN_LOCAL) {
-----------------------------------------------------------------------------------------
而此时这个被删除的ifa通过inet_addr_type判断时一定会返回RTN_UNICAST类型,从而命中了!=RTN_LOCAL分支,这正是我对fib_del_ifaddr感到疑惑的地方,为啥要这样做? 个人理解:
ip和路由条目之间没有必然联系(自动添加和删除的条目除外),完全可以人为的在local route table里面增加路由,导致最后一个ip被删除之后,local route table查找依然命中。
另外,对于loopback类型的接口,fib_add_ifaddr自动添加内核路由的时候,并不是往main表中添加的,而是往local表中添加的。
所以,如果添加了
ip addr add dev lo 1.1.1.1/16
ip addr add dev lo 1.1.1.2/24
local表中会有 1.1.0.0/16 和 1.1.1.0/24 两个非广播路由
当删除1.1.1.2/24的时候,!OK&LOCAL_OK仍然成立,1.1.1.0/24被删除,但是1.1.0.0/16仍然存在,所以还会返回RTN_LOCAL 本帖最后由 jiufei19 于 2015-04-27 21:59 编辑
nswcfd 发表于 2015-04-27 13:05 static/image/common/back.gif
个人理解:
ip和路由条目之间没有必然联系(自动添加和删除的条目除外),完全可以人为的在local route...
虽然我目前仍然无法理解fib_del_ifaddr函数在这个问题上的处理逻辑究竟是什么,不过似乎可以看成是这样一种基调:
------------------------------------------------------------------------------------------------------------
关于这里为啥要调用inet_addr_type进行继续判断的理由,目前可以暂时这样认为:当删除了某个local地址后,对应的RTN_LOCAL路由也被fib_magic函数在local表中所删除,于是系统需要进一步再通过inet_addr_type在local路由表中再次搜索是否真的再也没有这样的RTN_LOCAL路由了,如果是,则!=RTN_LOCAL成立,于是可以进一步去删除其他相关信息(fib_info和fib_nh),否则表示在local表中仍然可以找到对应此被删除ip的RTN_LOCAL路由存在,则不能进一步删除其他相关信息(fib_info和fib_nh)
页:
[1]
2