加入收藏 | 设为首页 | 会员中心 | 我要投稿 云计算网_宿迁站长网 (https://www.0527zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

Linux内核分析 - 网络[十五]:陆由表[再议]

发布时间:2016-01-26 04:03:25 所属栏目:Linux 来源:网络整理
导读:内核版本:2.6.34 陆由表作为三层协议的核心数据结构,理解它是至关重要的。前面已经分析过路由表,有兴趣的可以参考: 第一篇:路由表 http://blog.csdn.net/q

内核版本:2.6.34

陆由表作为三层协议的核心数据结构,理解它是至关重要的。前面已经分析过路由表,有兴趣的可以参考:

第一篇:路由表 http://blog.csdn.net/qy532846454/article/details /6423496

分析了路由表的基本数据结构和基本操作

第二篇:路由表使用 http://blog.csdn.net/qy532846454/article/details /6726171

分析了路由表的基本使用

这次将以更实际的例子来分析过程中路由表的使用情况,注意下文都是对路由缓存表的描述,因为路由表在配置完网卡地址后就不会再改变了(除非人为的去改动),测试环境如下图:

Linux内核分析 - 网络[十五]:陆由表[再议]

两台主机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来查看路由缓 存表,下图是执行上述操作后得到的结果:

Linux内核分析 - 网络[十五]:陆由表[再议]

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)) {     
…..     
}

(编辑:云计算网_宿迁站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!