Linux内核分析 - 网络[十五]:陆由表[再议]
内核版本:2.6.34 陆由表作为三层协议的核心数据结构,理解它是至关重要的。前面已经分析过路由表,有兴趣的可以参考: 第一篇:路由表 http://blog.csdn.net/qy532846454/article/details /6423496 分析了路由表的基本数据结构和基本操作 第二篇:路由表使用 http://blog.csdn.net/qy532846454/article/details /6726171 分析了路由表的基本使用 这次将以更实际的例子来分析过程中路由表的使用情况,注意下文都是对路由缓存表的描述,因为路由表在配置完网卡地址后就不会再改变了(除非人为的去改动),测试环境如下图: 两台主机Host1与Host2,分别配置了IP地址192.168.1.1与192.168.1.2,两台主机间用网线直连。在两台主机上分别执行 如下操作: 1. 在Host1上ping主机Host2 2. 在Host2上ping主机 Host1 很简单常的两台主机互ping的例子,下面来分析这过程中路由表的变化,准备说是路由缓存 的变化。首先,路由缓存会存在几个条目?答案不是2条而是3条,这点很关键,具体可以通过/proc/net/rt_cache来查看路由缓 存表,下图是执行上述操作后得到的结果: brcm0.1是Host主机上的网卡设备,等同于常用的eth0,lo是环路设备。对结果稍加分析 ,可以发现,条目1和条目2是完全一样的,除了计数的Use稍有差别,存在这种情况的原因是缓存表是以Hash表的形式存储的, 尽管两者内容相同,在实际插入时使用的键值是不同的,下面以Host2主机的路由缓存表为视角,针对互ping的过程进行逐一分 析。 假设brcm0.1设备的index = 2 步骤0:初始时陆由缓存为空 步骤1:主机Host1 ping 主机Host2 Host2收到来自Host1的echo报文(dst = 192.168.1.2, src = 192.168.1.1) 在报文进入IP层后会查询路由表,以确定报文的接收方式,相应调用流程: ip_route_input() -> ip_route_input_slow() 在ip_route_input()中查询路由缓存,使用的 键值是[192.168.1.2, 192.168.1.1, 2, id],由于缓存表为空,查询失败,继续走ip_route_input_slow()来创建并插入新的缓 存项。 hash = rt_hash(daddr, saddr, iif, rt_genid(net)); 在 ip_route_input_slow()中查询路由表,因为发往本机,在会LOCAL表中匹配192.168.1.2条目,查询结果res.type==RTN_LOCAL。 if ((err = fib_lookup(net, &fl, &res)) != 0) { if (!IN_DEV_FORWARD(in_dev)) goto e_hostunreach; goto no_route; } 然后根据res.type跳转到local_input代码段,创建新的路由缓存项,并插入陆由缓存。 rth = dst_alloc(&ipv4_dst_ops); …… rth->u.dst.dev = net->loopback_dev; rth->rt_dst = daddr; rth->rt_src = saddr; rth->rt_gateway = daddr; rth->rt_spec_dst = spec_dst; (spec_dst=daddr) …… hash = rt_hash(daddr, saddr, fl.iif, rt_genid(net)); err = rt_intern_hash(hash, rth, NULL, skb, fl.iif); 因此插入的第一条缓存信息如下: Key = [dst = 192.168.1.2 src = 192.168.1.1 idx = 2 id = id] Value = [Iface = lo dst = 192.168.1.2 src = 192.168.1.1 idx = 2 id = id ……] 步骤2:主机Host2 发送 echo reply报文给主机 Host1 (dst = 192.168.1.1 src = 192.168.1.2) 步骤2是紧接着步骤1的 ,Host2在收到echo报文后会立即回复echo reply报文,相应调用流程: icmp_reply() -> ip_route_output_key() -> ip_route_output_flow() -> __ip_route_output_key() -> ip_route_output_slow() - > ip_mkroute_output() -> __mkroute_output() 在icmp_reply()中生成稍后路由查找中的 关键数据flowi,可以看作查找的键值,由于是回复已收到的报文,因此目的与源IP地址者是已知的,下面结构中 daddr=192.168.1.1,saddr=192.168.1.2。 struct flowi fl = { .nl_u = { .ip4_u = { .daddr = daddr, .saddr = rt->rt_spec_dst, .tos = RT_TOS(ip_hdr(skb)->tos) } }, .proto = IPPROTO_ICMP }; 在__ip_route_output_key()时会查询路由缓存表,查询的键值是[192.168.1.1, 192.168.1.2, 0, id],由于此时路由缓存中只有一条刚刚插入的从192.168.1.1->192.168.1.2的缓存项,因而查询失败,继 续走ip_route_output_slow()来创建并插入新的缓存项。 hash = rt_hash(flp->fl4_dst, flp->fl4_src, flp- >oif, rt_genid(net)); 在ip_route_input_slow()中查询路由表,因为在同一网段, 在会MAIN表中匹配192.168.1.0/24条目,查询结果res.type==RTN_UNICAST。 if (fib_lookup(net, &fl, &res)) { ….. } (编辑:应用网_丽江站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |