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

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

日期:2017/3/1 11:09:17   编辑:Linux內核
內核版本:2.6.34
前篇路由表http://www.linuxidc.com/Linux/2011-05/36066.htm
說明了路由表的結構及路由表的創建。下面是一些路由表的使用的細枝末節,作補充說明。
路由可以分為兩部分:路由緩存(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. 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()繼續向上傳遞。

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

Copyright © Linux教程網 All Rights Reserved