内核版本:2.6.34
      前篇路由表http://blog.csdn.net/qy532846454/article/details/6423496说明了路由表的结构及路由表的创建。下面是一些路由表的使用的细枝末节,作补充说明。
      路由可以分为两部分:路由缓存(rt_hash_table)和路由表()
      路由缓存顾名思义就是加速路由查找的,路由缓存的插入是由内核控制的,而非人为的插入,与之相对比的是路由表是人为插入的,而非内核插入的。在内核中,路由缓存组织成rt_hash_table的结构。

下面是一段IP层协议的代码段[net/ipv4/route.c],传入IP层的协议在查找路由时先在路由缓存中查找,如果已存在,则skb_dst_set(skb, &rth->u.dst)并返回;否则在路由表中查询。

[cpp] view plaincopy
  1. hash = rt_hash(daddr, saddr, iif, rt_genid(net));
  2. rcu_read_lock();
  3. for (rth = rcu_dereference(rt_hash_table[hash].chain); rth;
  4. rth = rcu_dereference(rth->u.dst.rt_next)) {
  5. if (((rth->fl.fl4_dst ^ daddr) |
  6. (rth->fl.fl4_src ^ saddr) |
  7. (rth->fl.iif ^ iif) |
  8. rth->fl.oif |
  9. (rth->fl.fl4_tos ^ tos)) == 0 &&
  10. rth->fl.mark == skb->mark &&
  11. net_eq(dev_net(rth->u.dst.dev), net) &&
  12. !rt_is_expired(rth)) {
  13. dst_use(&rth->u.dst, jiffies);
  14. RT_CACHE_STAT_INC(in_hit);
  15. rcu_read_unlock();
  16. skb_dst_set(skb, &rth->u.dst);
  17. return 0;
  18. }
  19. RT_CACHE_STAT_INC(in_hlist_search);
  20. }
  21. rcu_read_unlock();

在ip_route_input()中查询完陆由缓存后会处理组播地址,如果是组播地址,则下面判断会成功:ipv4_is_multicast(daddr)。
然后执行ip_route_input_mc(),它的主要作用就是生成路由缓存项rth,并插入缓存。rth的生成与初始化只给出了input函数的,其它略去了,可以看出组播报文会通过ip_local_deliver()继续向上传递。

[cpp] view plaincopy
  1. rth->u.dst.input= ip_local_deliver;
  2. hash = rt_hash(daddr, saddr, dev->ifindex, rt_genid(dev_net(dev)));
  3. return rt_intern_hash(hash, rth, NULL, skb, dev->ifindex);

路由表又可以分为两个:RT_TABLE_LOCAL和RT_TABLE_MAIN
        RT_TABLE_LOCAL存储目的地址是本机的路由表项,这些目的地址就是为各个网卡配置的IP地址;
        RT_TABLE_MAIN存储到其它主机的路由表项;
      显然,RT_TABLE_MAIN路由表只有当主机作为路由器时才有作用,一般主机该表是空的,因为主机不具有转发数据包的功能。RT_TABLE_LOCAL对主机就足够了,为各个网卡配置的IP地址都会加入RT_TABLE_LOCAL中,如为eth1配置了1.2.3.4的地址,则RT_TABLE_LOCAL中会存在1.2.3.4的路由项。只有本地的网卡地址会被加入,比如lo、eth1。IP模块在初始化时ip_init() -> ip_rt_init() - > ip_fib_init()会注册notifier机制,当为网卡地址配置时会执行fib_netdev_notifier和fib_inetaddr_notifier,使更改反映到RT_TABLE_LOCAL中。

[cpp] view plaincopy
  1. register_netdevice_notifier(&fib_netdev_notifier);
  2. register_inetaddr_notifier(&fib_inetaddr_notifier);

而当在路由缓存中没有查找到缓存项时,会进行路由表查询,还是以IP层协议中的代码段为例[net/ipv4/route.c],fib_lookup()会在MAIN和LOCAL两张表中进行查找。

[cpp] view plaincopy
  1. if ((err = fib_lookup(net, &fl, &res)) != 0) {
  2. if (!IN_DEV_FORWARD(in_dev))
  3. goto e_hostunreach;
  4. goto no_route;
  5. }

如果主机配置成了支持转发,则无论在路由表中找到与否,都会生成这次查询的一个缓存,包括源IP、目的IP、接收的网卡,插入路由缓存中:

[cpp] view plaincopy
  1. hash = rt_hash(daddr, saddr, fl.iif, rt_genid(net));
  2. err = rt_intern_hash(hash, rth, NULL, skb, fl.iif);

不同的是,如果在路由表中查询失败,即数据包不是发往本机,也不能被本机转发,则会设置插入路由缓存的缓存项u.dst.input=ip_error,而u.dst.input即为IP层处理完后向上传递的函数,而ip_error()会丢弃数据包,被发送相应的ICMP错误报文。不在路由表中的路由项也要插入路由缓存,这可以看作路由学习功能,下次就可以直接在路由缓存中找到。

[cpp] view plaincopy
  1. rth->u.dst.input= ip_error;
  2. rth->u.dst.error= -err;
  3. rth->rt_flags    &= ~RTCF_LOCAL;

但如果主机不支持转发,即没有路由功能,则只有在找到时才会添加路由缓存项,都不会生成路由缓存项。这是因为在LOCAL表中没有找到,表明数据包不是发往本机的,此时缓存这样的路由项对于主机的数据包传输没有一点意义。它只需要知道哪些数据包是发给它的,其余的一律不管!

路由查询整合起来,就是由ip_route_input()引入,然后依次进行路由缓存和路由表查询,并对路由缓存进行更新。路由缓存在每个数据包到来时都可能发生更新,但路由表则不一样,只能通过RTM机制更新,LOCAL表是在网卡配置时更新的,MAIN表则是由人工插入的(inet_rtm_newroute)。
       ip_route_input()
         - 路由缓存查询
         - 路由表查询:ip_route_input_slow() -> fib_lookup()

Linux内核分析 - 网络[四补]:路由表补充相关推荐

  1. Linux内核分析 - 网络[四]:路由表

    路由表 在内核中存在路由表fib_table_hash和路由缓存表rt_hash_table.路由缓存表主要是为了加速路由的查找,每次路由查询都会先查找路由缓存,再查找路由表.这和cache是一个道理 ...

  2. linux内核等价多路径路由,Linux内核分析 - 网络[四]:路由表

    路由表的创建 inet_init() -> ip_init() -> ip_fib_init() -> fib_net_init() -> ip_fib_net_init()[ ...

  3. Linux内核分析 - 网络[八补]:IP协议补充

    内核版本:2.6.34 在前一篇"IP协议"中对报文接收时IP层的处理进行了分析,本篇分析将针对报文发送时IP层的处理.       传输层处理完后,会调用ip_push_pend ...

  4. linux内核分析 网络九,“Linux内核分析”实验报告(九)

    一 Linux内核分析博客简介及其索引 本次实验简单的分析了计算机如何进行工作,并通过简单的汇编实例进行解释分析 在本次实验中 通过听老师的视频分析,和自己的学习,初步了解了进程切换的原理.操作系统通 ...

  5. Linux内核分析(四)之“暗流涌动”

    一.拨云见日 身为程序员,我们绕不开系统调用,但是我们往往都是通过一个"中间人"--库函数与其打交道. 我们调用一个库函数也许看起来非常简单,但是其真正的实现细节,并非我们看起来那 ...

  6. Linux内核分析 - 网络[十六]:TCP三次握手

    内核:2.6.34       TCP是应用最广泛的传输层协议,其提供了面向连接的.可靠的字节流服务,但也正是因为这些特性,使得TCP较之UDP异常复杂,还是分两部分[创建与使用]来进行分析.这篇主要 ...

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

    内核版本:2.6.34 陆由表作为三层协议的核心数据结构,理解它是至关重要的.前面已经分析过路由表,有兴趣的可以参考:       第一篇:路由表 http://blog.csdn.net/qy532 ...

  8. Linux内核分析 - 网络[十四]:IP选项

    内核版本:2.6.34       在发送报文时,可以调用函数setsockopt()来设置相应的选项,本文主要分析IP选项的生成,发送以及接收所执行的流程,选取了LSRR为例子进行说明,主要分为选项 ...

  9. Linux内核分析 - 网络[十一]:ICMP模块

    内核版本:2.6.34 ICMP模块比较简单,要注意的是icmp的速率限制策略,向IP层传输数据ip_append_data()和ip_push_pending_frames(). 在net/ipv4 ...

最新文章

  1. Docker数据卷管理
  2. ios开发 ajax hook,IOS中的网络拦截总结
  3. java8 stream中的惰性求值
  4. 使用拦截器分析Java EE应用程序的性能下降/提高
  5. 工作290:js日期操作
  6. 干货|基于深度学习的目标检测算法面试必备(RCNN~YOLOv5)
  7. 【论文阅读】Network In Network
  8. 魅族路由器极速版刷机_[技术贴]路由器刷pandavan固件教程——以魅族路由器极速版为例...
  9. 如何全面系统的自学Java?(附2022最新整理Java学习路线)
  10. 忍无可忍?英特尔执行副总裁撰文《高通的诡辩被戳穿了》指责高通
  11. android卡刷教程,卡刷是什么意思?安卓系统卡刷教程详解
  12. tilemap 菱形_带高度的isometric tile map 斜45度 2d 地图制作
  13. Invalid bound statement (not found)错误的原因和解决办法
  14. 单元识别码是什么意思_NLPer入门指南 | 完美第一步
  15. 苏州大学应用技术学院计算机二级,苏州大学应用技术学院怎么样_苏州大学一本与二本有什么差别...
  16. 服务器线路有几种分类?
  17. Dialog确认框重复提交的问题
  18. 递归函数详细版(Java)
  19. Zynq-7000 PS重配置PL
  20. 免费空间2015大主机免费的时代

热门文章

  1. java 栈的用法_让Java程序员再次生机勃勃,还是技术的力量
  2. Mysql通过一个限制条件,查出多条不同的记录
  3. jackson 复杂对象集合 的几种简单转换
  4. 常用的正则表达式方法1
  5. javascrpt 继承
  6. MySQL列类型之——数值类型
  7. 不附加数据库 ASP.NET调用.sql文件
  8. 聪明的苹果——iPhone 3GS。
  9. android 禁止屏幕放大缩小,禁止APP内Webview页面跟随系统缩放字号
  10. 城乡投票源码php_响应式投票系统(支持微信、手机) php版 v3.2