下面是一段IP層協議的代碼段[net/ipv4/route.c],傳入IP層的協議在查找路由時先在路由緩存中查找,如果已存在,則skb_dst_set(skb, &rth->u.dst)並返回;否則在路由表中查詢。
在ip_route_input()中查詢完陸由緩存後會處理組播地址,如果是組播地址,則下面判斷會成功:ipv4_is_multicast(daddr)。
然後執行ip_route_input_mc(),它的主要作用就是生成路由緩存項rth,並插入緩存。rth的生成與初始化只給出了input函數的,其它略去了,可以看出組播報文會通過ip_local_deliver()繼續向上傳遞。
路由表又可以分為兩個: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中。
而當在路由緩存中沒有查找到緩存項時,會進行路由表查詢,還是以IP層協議中的代碼段為例[net/ipv4/route.c],fib_lookup()會在MAIN和LOCAL兩張表中進行查找。
如果主機配置成了支持轉發,則無論在路由表中找到與否,都會生成這次查詢的一個緩存,包括源IP、目的IP、接收的網卡,插入路由緩存中:
不同的是,如果在路由表中查詢失敗,即數據包不是發往本機,也不能被本機轉發,則會設置插入路由緩存的緩存項u.dst.input=ip_error,而u.dst.input即為IP層處理完後向上傳遞的函數,而ip_error()會丟棄數據包,被發送相應的ICMP錯誤報文。不在路由表中的路由項也要插入路由緩存,這可以看作路由學習功能,下次就可以直接在路由緩存中找到。
但如果主機不支持轉發,即沒有路由功能,則只有在找到時才會添加路由緩存項,都不會生成路由緩存項。這是因為在LOCAL表中沒有找到,表明數據包不是發往本機的,此時緩存這樣的路由項對於主機的數據包傳輸沒有一點意義。它只需要知道哪些數據包是發給它的,其余的一律不管!
路由查詢整合起來,就是由ip_route_input()引入,然後依次進行路由緩存和路由表查詢,並對路由緩存進行更新。路由緩存在每個數據包到來時都可能發生更新,但路由表則不一樣,只能通過RTM機制更新,LOCAL表是在網卡配置時更新的,MAIN表則是由人工插入的(inet_rtm_newroute)。
ip_route_input()
- 路由緩存查詢
- 路由表查詢:ip_route_input_slow() -> fib_lookup()