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)
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 個,但須滿足下列條件:
- 資料的數目大於 hash table 一半大小。
- 目前的 hash table 大小小於 1024。
- 網路位置長度 z 小於 32 或 2z 大於 hash table 的大小。
3.2 route 系統讀取 FIB 資料
route 系統讀取資料:- 將資料存到 fib_result 資料結構裡。
- 系統需檢查找到的資料是否符合條件,若不符合則繼續找,直到找完整個 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() 的函式便能使用這些資訊來建立正確的 cacheRT_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