当前位置: 代码迷 >> 综合 >> Linux IP Routing
  详细解决方案

Linux IP Routing

热度:39   发布时间:2023-12-17 06:15:00.0
Linux IP Routing

1、網路的連結


根據實際的網路架構可以大略的粗分以下兩種
Ⅰ?直接連接


Ⅱ?間接連接(經過 router 轉送)


Ⅰ為直接的電腦連結,或者是同個網域的電腦,無論是經 bridge 或者是 hub,
因為這些裝置只是將資料無條件的傳輸到彼端,Ⅱ 則是經由路由器(router)
轉送,直接轉送到另一台電腦,或者是再經由另一台路由器輾轉傳送到彼端,
為求精簡,我們只用一台 router 代表他不是直接的連結。
因為不是直接的連結,我們中間的 router 必需經由某些特定的規則,將這些
local 端的資料傳送到正確的路徑。


2.1、Linux IP Routing 的功能


IP Routing 是網路連結的重要機制,linux 的 ip routing 主要的功能為



  • Ⅰ、確定來源 ip packet 是自己的,並將此 packet 往上層推送,依 TCP/IP
    的結構而言,IP 的上層也就是 TCP,在 linux 中是交由 ip_local_deliver()
    處理往上丟。

  • Ⅱ、廣播封包(broadcast),自己為廣播領域(broadcast domain,例如同一個 LAN)
    的一員,此封包的目的端也是自己,所以跟 Ⅰ 相同的結果是,也交由
    ip_local_deliver() 處理。

  • Ⅲ、多點傳送的接受端(multcast),這項特別的是,他並不會依所設定的靜態路由
    來做查詢的動作,因為 multicast 的架構不適合設到 FIB 裡去,因為來源的
    點不確定是哪個 ip range,所以是直接建立 routing cache。

  • Ⅳ、需要轉送接收到的封包,機器本身是做 router 或者是 NAT 的工作,依據來源
    的封包來決定往哪張 NIC 送出。

  • Ⅴ、local 端的封包送出,在 linux 裡,local 端的輸出跟轉送所處理的方式是
    分開的。


以上除了 Ⅴ 以外,都是處理往自己送的封包情形


2.2、使用者所見的 routing 機制


Linux 在決定 routing 時有兩個機制,一個是 FIB(forwarding information table)
一個是依據 FIB 動態產生的 routing cache(除 multicast 外),FIB 是利用 route(man 8 route)
來靜態的指定 route table,而 routing cache 所做的是利用 hash 機制加快決定路由時
的反應速度,因為每個 IP 封包的進出口都需要經由 routing 這個關口,所以這裡的速度
是額外的重要


範例:

靜態的 routing table


# route -F

Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.1.3 * 255.255.255.255 UH 0 0 0 eth0
140.123.103.156 * 255.255.255.255 UH 0 0 0 eth1
192.168.1.0 * 255.255.255.0 U 0 0 0 eth0
140.123.0.0 * 255.255.0.0 U 0 0 0 eth1
127.0.0.0 * 255.0.0.0 U 0 0 0 lo
default CSIE-C5505 0.0.0.0 UG 0 0 0 eth1

  • Flags 有下列幾種
  • U - 此Routing table 可使用

  • G - 此路由為通往路由器

  • H - 直接通往一主機的路由,此路由的最終目的

  • D - 此路由因轉向而產生

  • M - 此路由因轉向而改變


動態產生的 routing cache


# route -C

Kernel IP routing cache
Source Destination Gateway Flags Metric Ref Use Iface
infomedia.cs.cc 140.123.103.255 140.123.103.255 ri 0 6 0 eth1
CSIE-C5505 not-a-legal-add not-a-legal-add ibl 0 796989 0 lo
filter.cs.ccu.e 140.123.103.255 140.123.103.255 ri 0 265 0 eth1
CSIE-C5505 ALL-SYSTEMS.MCA ALL-SYSTEMS.MCA ml 0 7273 0 lo
vivi.cs.ccu.edu 140.123.103.255 140.123.103.255 ri 0 7 0 eth1
gigi.cs.ccu.edu 140.123.103.255 140.123.103.255 ri 0 3 0 eth1
scoap.cs.ccu.ed 140.123.103.255 140.123.103.255 ri 0 12 0 eth1
bist.cs.ccu.edu 140.123.103.255 140.123.103.255 ri 0 86 0 eth1
macgyver.cs.ccu 140.123.103.255 140.123.103.255 ri 0 11 0 eth1

示意圖




3、FIB(Forwarding Information Base)

FIB 全名為 Forwarding Information Base,裡頭儲存了『靜態』的繞送資訊。所謂的靜態是指裡頭的資料只能夠由管理者所修改(使用 IO control function )。相對的,屬於動態的 rtable 可以由 ICMP 封包來修改裡頭的繞送資訊。

3.1 FIB 實作--資料結構簡介

在 Linux 2.4 核心中允許多個 FIB 存在,讓系統管理者可以更靈活、更快速地管理路由系統(routing system)的運作。不過一般情況下並不需要使用多個 FIB ,只用了兩個:main table 和 local table。local table 儲存本地端所屬 IP 之間的封包繞送資訊;其他繞送資訊則儲存在 main table 中。

在核心程式碼中,FIB 使用了結構 fib_table 來定義。在 fib_table 的成員中包括了『函數成員』和『資料成員』,所有對 fib_table 裡頭資料的讀取修改都必須透過它的函數成員來達成。fib_table 的最後一個資料成員 tb_data,為一指向結構 fn_hash 的指標。結構 fn_hash 包含了一組(三十三個)指向 hash table 的指標 fn_zones[33],也包含了一個 hash list:fn_zone_list。使用 fn_zones[] 分類是為了快速處理( hash search )。繞送資訊依照其 IP 位置中的網域位置(另一部份為主機位置)的長度:z,被放到 fn_zones[z] 裡頭,而 0 <= z <= 32。z 即為 netmask 的長度。

fn_zone_list 則包含了有資料在裡頭的 fn_zone,依照網域位置長度『由大到小』串起來。這樣的一個串列在找尋繞送資訊時會有明顯得好處。在使用 fib_lookup() 找尋繞送資訊時,呼叫了 fib_table -> tb_lookup() 對所有的 fn_zone 做搜尋。tb_lookup() 預設為 fn_hash_lookup(),在檔案 net/ipv4/fib_hash.c中。 fn_hash_lookup() 裡頭依大小順序呼叫 fn_zone_list 所含的 fn_zone 做搜尋的動作,直到找得所要的繞送資訊為止。也就是說,網路位置長度大的繞送資訊會先被找到,符合我們的期待。舉例來說,送往特定 IP 的規則優先於送往此 IP 所屬網域的規則。

結構 fib_node 紀錄了屬於繞送資訊的基本部份,像是 fn_tos,fn_type,fn_scope,另外紀錄了指向 fib_info 結構的指標 fn_info。fib_info 則紀錄了繞送資訊的詳細部份,包括了 output device 、 next hop router、gateway ...等等。所有的 fib_info 形成一個雙向串列,fib_info_list 指向此雙向串列的頭。

對於網域位置長度不為零的 hash table (fn_zones[i], i!=0),其預設的 slot 個數為 16。如果旗標 CONFIG_IP_ROUTE_LARGE_TABLES 有設定的話,則 slot 的個數可以視情況增加到 256 或者 1024 個,但須滿足下列條件:
  1. 資料的數目大於 hash table 一半大小。
  2. 目前的 hash table 大小小於 1024。
  3. 網路位置長度 z 小於 32 或 2z 大於 hash table 的大小。
底下是詳細的 FIB 資料結構關係圖。





3.2 route 系統讀取 FIB 資料

route 系統讀取資料:
  1. 將資料存到 fib_result 資料結構裡。
  2. 系統需檢查找到的資料是否符合條件,若不符合則繼續找,直到找完整個 FIB 為止。
函數流程:



3.2.1
  • 在 net/ipv4/fib_rules.c 的部份,由函數 fib_lookup() 負責。
  • 一開始先根據傳入的條件,找到合適的 fib_rule 使用。主要的比對條件有三個:目的地位置,來源位置,使用的網路介面。預設的 fib_rule 比對順序為 local_rule , main_rule , default_rule ,也可以說是 fib_rule 的優先順序。
  • 找到合適的 fib_rule 之後,根據 fib_rule->r_action 決定繼續往下執行( RTN_UNICAST, RTN_NAT ),或者跳出並回傳錯誤值。
  • 繼續往下執行。根據 fib_rule->r_table 決定所要尋找的 fib_table之後,呼叫 tb->tb_lookup() 找尋資料。tb->tb_lookup() 實際上就是 fn_hash_lookup(),回傳的資料大部分是由這個函數所設定,除了 fib_result->r 設定為剛剛所找到的 fib_rule(即合適的 fib_rule) 。

3.2.2
  • 在 net/ipv4/fib_hash.c 的部份,由函數 fn_hash_lookup() 負責。
  • 在這裡用了兩個迴圈,外層迴圈用來搜尋所有屬於此 fib_table 的 fn_zone,內層迴圈搜尋 hash table 其中一個 slot。
  • 為什麼可以直接搜尋 hash table 其中一個 slot ?因為可以用函數 fz_key() 加上兩個參數:目的地位置和所找的 fz_zone,算出 hash key。然後用函數 fz_chain() 把 slot 抓出來。這就是使用 hash table 的好處,可以加快資料找尋的速度。
  • 找到符合位置(IP位置)的 fib_node 之後,做狀態檢查和回傳值設定。其中呼叫 fib_semantic_match() 做狀態檢查和回傳值設定, fib_semantic_match() 在檔案 net/ipv4/fib_semantics.c 中。

以下為回傳值設定表:
fib_result 成員
設定值
設定函數
r (struct fib_rule)
policy
fib_lookup()
type
f->fn_type
fn_hash_lookup()
scope
f->fn_scope
fn_hash_lookup()
prefixlen (mask length)
fz->fz_order
fn_hash_lookup()
fi (struct fib_info)
fi (傳入的參數)
fib_semantic_match()
nh_sel
nhsel(全域變數?)
fib_semantic_match()
prefix
無,prefix = dst AND operation with it's mask

4、FIB Rules


FIB rules 是決定哪個存取哪個 FIB table 的重要機制, fib_rules 一開始
的初始化有三個 rules - local_rule,main_rule,default_rule,依據順序為




fib_rule 的對外窗口是 fib_lookup() 這隻函式,在 route.c 裡,需要對 FIB
作查詢時便利用他,比對 src/src_mask/dst/dst_mask/tos/iif 來找到所需要的
fib_rules 進而取得 fib_table 的 id,利用 fib_table 的函式,來查詢 fib_result,
route.c 裡所呼叫 fib_lookup() 的函式便能使用這些資訊來建立正確的 cache




RT_TABLE_* 為所對應的 fib_table,這個機制最主要是要來支援 routing msg
來建立 fib_rules <-> fib_table 的對應,會依據 r_preference 來建立 priority
的關係,以致於會先得到正確的 fib_rule <-> fib_table 的對應


5、FIB 的快取機制 - routing cache


5.1 Routing cache 的基本結構


因為 Routing 速度是非常重要的一個關鍵,所以我們在此利用 hash 建立
一個快取的機制,這就 Linux Routing Cache。
使用的 hash key 為 source address、target address與 TOS,整個 cache 的
結構如下:




而每個圈圈代表的是一個 rtable 的 structure,rtable 中的的 union u,其中的
rt_next 指標,跟 dst_entry dst 中第一個 next 指標,剛好相對應,利用這個技巧
形成了一個奇妙的關係


示意圖




5.2 Output Routing


用途:當自己的機器產生新的封包要往外推送時,也就是接到上層 ip_queue_xmit(),
這時 ip_queue_xmit() 會呼叫 ip_route_output() 來決定推送的目標,kernel 2.4 中多了一層
界面,也就是 ip_route_output() ~= ip_route_output_key(),也就是 ip_route_output() 包裝好
rt_key 後會 call ip_route_output_key(), ip_route_output() → ip_route_output_key(),
其中大略的流程如下:




在 ip_route_output () 後就是 IP routing 的整個機制。




一開始檢查是否已經存在 Routing Cache 中,有的話就直接回傳存在的 routing Cache,
如果不存在的話,則使用 ip_route_output_slow(),使用 fib_lookup() 從 fib_rules 中查詢,
fib_rules 主要是用來作 multipath 用的,一般而言,只會用到其 main/local/default rules,
每個 rules 都會有其對應的 fib_table, 利用 tb->tb_lookup( ) 來查詢 FIB中的資訊,
FIB 也就是之前所提的靜態設定的 routing。從 FIB 中取得所需要的資訊後(fib_result),
便利用這些資訊設定 Routing Cache 的 entry,主要的 output function 為 ip_output()
填完資訊後便利用 rt_intern_hash( ) 將所產生的 entry 掛進 rt_hash_bucket[] 裡,
並 bind_neighbour, bind_neighbour 也就是一般情形下會出現的 arp request,當我們要與直接
的一台主機連線時,傳送的是第二層的 driver,需要的是 mac address,bind_neightbour 時發現
目的位址沒有 IP <-> MAC 的對應(arp cache) 便會主動利用 arp_send() 發出 arp request
來取得 MAC address 而到這裡,Output Routing 便算結束。


5.3 Input Routing


Input 的 Routing 主要是因為 linux 機器接收了外來的 IP 封包,利用來源
的位址來決定怎麼處理這個封包,與 Output routing 相同的是 fib_lookup( ) 之後
的程序,之前需要判斷是不是 multicast 位址,因為 multicast 不需要利用fib_lookup()
也就是 FIB 資訊,所以利用另一個函數 ip_route_input_mc( ) 來處理。
而其它的一般而言,有轉送,接收與丟棄三種處理方式:

1.接收:本身的 linux 機器就是目的位址,這種封包有二種可能:廣播與unicast,
這兩種的傳送對向都是自己,因為 layer 只負責傳送資訊,所以必需將這些資訊往
上層傳,也就是 TCP 層,利用 input fuction 交給上層處理,這裡的 input function
則為 ip_local_deliver( )
2.轉送:本身的機器作用為 router,不論是 NAT 或者是 router,都必要將來源的封包正確
轉送到目的位址,這裡跟 output routing 不一樣的是,他不需要再進行一下檢查 routing
的動作,而是直接交由 ip_forward( ) 來處理。
3.丟棄:來源的封包是不正確的位址或者是不需要的東西,這裡是交給 ip_error( ) 來處理。

依這三種不一樣的程序處理完後,除了不正確的外,其它跟 ip_route_output( ) 一樣,交給
rt_intern_hash( ) 來處理增加 entry 到 rt_hash_bucket[] 的事。



5.3.1 Input Routing for local/broadcast


這部份是負責要接受外部往上的封包,所以在 fib_lookup() 後,依據所得的 fib_result
來填入 rtable 中,而這部份所負責往下接收的 input function 是 ip_local_deliver()
ip_local_deliver() 中再以 ip 封包中不同的形態來決定不同的函式接受處理。



5.3.2 Input Routing for forwarding


在此 linux 機器伴演著轉送者的角色,input function 則指定為 ip_forward()
因為進入跟輸出的 dev 可能不一樣,所以還必需做處理,如封包重組及設定下一個點(next_hop)等。
以下為大約的流程:



5.3.3 Input Routing for multicast


在 ip_route_input() 中,若是 class D(ip range 為 224.0.0.0/240.0.0.0) 則為 multicast
的 ip 封包,這時就交給 ip_route_input_mc() 做處理。因為也是讓 local 接受的所以
他的 input function 跟 local/broadcast 一樣,皆是 ip_local_deliver()


5.4 Routing Cache 的維護機制


因為 routing cache 的空間必需要有效利用,而且有些 routing cache 不常用到,
我們也不能讓他佔在記憶體中佔位置,所以我們就要利用 timer 的機制來定時處理這些
過期的垃圾,這就是 garbage collection。


當系統一開機時,ip_rt_init() 這個 initial function 會註冊一個叫 rt_periodic_timer
的 timer,每 rt_periodic_timer 的時間呼叫 rt_periodic_timer() check 是否應該進行清理過期的
rtable,檢查 rt_may_expire() 的路徑..將之清理。


另一個 ip_rt_multicast_event() 是當 device 加入一個 multicast 群組,則呼叫
rt_cache_flush() 來將所有的 rtable reset