zoukankan      html  css  js  c++  java
  • Linux内核分析


    http://blog.csdn.net/qy532846454/article/details/6726171


          内核版本: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)并返回;否则在路由表中查询。

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

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

    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中。

    1. register_netdevice_notifier(&fib_netdev_notifier);  
    2. register_inetaddr_notifier(&fib_inetaddr_notifier);  

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

    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、接收的网卡,插入路由缓存中:

    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错误报文。不在路由表中的路由项也要插入路由缓存,这可以看作路由学习功能,下次就可以直接在路由缓存中找到。

    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()

  • 相关阅读:
    set集合
    作业(2)
    字典
    列表功能
    字符串
    while循环
    pb加密转换成C#
    NET在64位系統使用32位oracle客户端访问数据库
    SQL Server中查找包含某个文本的存储过程
    SQLServer 错误: 15404,无法获取有关 Windows NT 组/用户
  • 原文地址:https://www.cnblogs.com/ztguang/p/12644635.html
Copyright © 2011-2022 走看看