歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> Linux內核分析 - 網絡[四補]:路由表補充

Linux內核分析 - 網絡[四補]:路由表補充

日期:2017/3/3 16:38:21   编辑:Linux內核

內核版本: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   
       
    hash = rt_hash(daddr, saddr, iif, rt_genid(net));     
             
    rcu_read_lock();     
    for (rth = rcu_dereference(rt_hash_table[hash].chain); rth;     
         rth = rcu_dereference(rth->u.dst.rt_next)) {     
        if (((rth->fl.fl4_dst ^ daddr) |     
             (rth->fl.fl4_src ^ saddr) |     
             (rth->fl.iif ^ iif) |     
             rth->fl.oif |     
             (rth->fl.fl4_tos ^ tos)) == 0 &&     
            rth->fl.mark == skb->mark &&     
            net_eq(dev_net(rth->u.dst.dev), net) &&     
            !rt_is_expired(rth)) {     
            dst_use(&rth->u.dst, jiffies);     
            RT_CACHE_STAT_INC(in_hit);     
            rcu_read_unlock();     
            skb_dst_set(skb, &rth->u.dst);     
            return 0;     
        }     
        RT_CACHE_STAT_INC(in_hlist_search);     
    }     
    rcu_read_unlock();

在ip_route_input()中查詢完陸由緩存後會處理組播地址,如果是組播地址,則下面判斷會 成功:ipv4_is_multicast(daddr)。

然後執行ip_route_input_mc(),它的主要作用就是生成路由緩存項rth,並插入緩 存。rth的生成與初始化只給出了input函數的,其它略去了,可以看出組播報文會通過ip_local_deliver()繼續向上傳遞。

rth->u.dst.input= ip_local_deliver;     
hash = rt_hash(daddr, saddr, dev->ifindex, rt_genid(dev_net(dev)));     
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中。

register_netdevice_notifier(&fib_netdev_notifier);     
register_inetaddr_notifier(&fib_inetaddr_notifier);

而當在路由緩存中沒有查找到緩存項時,會進行路由表 查詢,還是以IP層協議中的代碼段為例[net/ipv4/route.c],fib_lookup()會在MAIN和LOCAL兩張表中進行查找。

if 

((err = fib_lookup(net, &fl, &res)) != 0) {     
    if (!IN_DEV_FORWARD(in_dev))     
        goto e_hostunreach;     
    goto no_route;     
}

如果主機配置成了支持轉發,則無論在路由表中找到與否,都會生成這次查詢的一個緩存,包括源IP、目的IP、接收 的網卡,插入路由緩存中:

hash = rt_hash(daddr, saddr, fl.iif, rt_genid(net));     
err = rt_intern_hash(hash, rth, NULL, skb, fl.iif);

不同的是,如果在路由表中查詢失敗,即數據包不是發往本 機,也不能被本機轉發,則會設置插入路由緩存的緩存項u.dst.input=ip_error,而u.dst.input即為IP層處理完後向上傳遞的 函數,而ip_error()會丟棄數據包,被發送相應的ICMP錯誤報文。不在路由表中的路由項也要插入路由緩存,這可以看作路由學 習功能,下次就可以直接在路由緩存中找到。

rth->u.dst.input= ip_error;     
rth->u.dst.error= -err;     
rth->rt_flags    &= ~RTCF_LOCAL;

但如果主機不支持轉發,即沒有路由功能,則只有在找到時才會添加路由 緩存項,都不會生成路由緩存項。這是因為在LOCAL表中沒有找到,表明數據包不是發往本機的,此時緩存這樣的路由項對於主 機的數據包傳輸沒有一點意義。它只需要知道哪些數據包是發給它的,其余的一律不管!

路由查詢整合起來,就是由 ip_route_input()引入,然後依次進行路由緩存和路由表查詢,並對路由緩存進行更新。路由緩存在每個數據包到來時都可能發 生更新,但路由表則不一樣,只能通過RTM機制更新,LOCAL表是在網卡配置時更新的,MAIN表則是由人工插入的 (inet_rtm_newroute)。

ip_route_input()

- 路由緩存查詢

- 路由表查詢:ip_route_input_slow() -> fib_lookup()

Copyright © Linux教程網 All Rights Reserved