Linux kernel路由機制分析(下)

2021-08-15 04:06:41 字數 3124 閱讀 1170

前面已經介紹過,ip層會在輸入和輸出兩個時候去呼叫路由部分**。輸入路由過程更為複雜一些也更具代表性,所以我們下面主要分析一下ip包輸入時的路由流程。

下圖描述了這個流程:

當有資料到達網路裝置的時候,會產生乙個中斷,中斷處理函式會呼叫驅動層的net_rx函式,net_rx進而產生個軟中斷進入net_rx_action函式,進而如是發現這個資料幀是ip包的話,它就呼叫ip協議層的ip_rcv函式,它進而又呼叫ip_rcv_finish函式。在這個函式,它呼叫路由**的ip介面函式ip_route_input進行路由。可以看到傳遞給路由**的引數有5個:skb ip包緩衝區,iph->daddr ip包的目的位址,iph->saddr ip包源位址,iph->tos 服務型別,dev 輸入的網路裝置。當這個ip_route_input函式返回時,就意味著路由工作已經結束,如果返回值是0,那麼就說明已經成功找到了路由。那麼這個路由查詢結果放在**呢?它就在skb->dst,它指向的就是查到的路由快取中的乙個結點。下邊通過呼叫skb->dst->input(skb)就可以對這個ip進行處理了。這個input是路由快取結點中的乙個函式指標,如果這個路由項表示**的,那麼這個指標實際上指向的是ip_local_deliver,而如果是傳送給本地的,那麼指向的是ip_forward。ip_local_deliver會將這個ip包進一步傳給上層協議層處理,ip_forward則會再將這個ip包從網路裝置傳送出去。

我們再來看一下路由的具體流程。

首先呼叫的是ip_route_input,它的任務主要是查路由快取,如果找到了那麼它給skb->dst賦值並返回,如是沒找到,它會呼叫ip_route_input_slow去查詢路由策略資料庫。

下面是經過簡化的**和注釋:

int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr,  u8  tos, struct net_device *dev)

int iif = dev->ifindex;

hash = rt_hash_code(daddr, saddr ^ (iif << 5), tos);

/* 遍歷hash table */

for (rth = rt_hash_table[hash].chain; rth; rth = rth->u.rt_next )

/* 如果有錯誤,則返回錯誤號,如果是-eagain或正數則查下一策略 */

if (err < 0 && err != -eagain) return err;

return -enetunreach;

fn_hash_lookup函式的主要功能即是對路由表的查詢。如下:

int fn_hash_lookup(struct fib_table *tb, const struct rt_key *key, struct fib_result *res)

/* 從大到小遍歷區域 */

for (fz = t->fn_zone_list; fz; fz = fz->fz_next) 即會跳到no_route處:

no_route:

rt_cache_stat

[smp_processor_id

()].in_no_route++;

spec_dst = inet_select_addr

(dev

, 0, rt_scope_universe);

res.type

= rtn_unreachable;

goto local_input;

它把res.type標記成rtn_unreachable然後跳到本地包情況的處理**,先是更新路由快取,然後遇到如下**:

if (res

.type

== rtn_unreachable)

rth->rt_type    = res

.type

;

goto intern;
即判斷如果res.type是rtn_unreachable標記,那麼給函式指標dst.input賦為ip_err,將dst.error賦為-err。然後插入到快取。

最後ip層呼叫的skb->dst->input實際上就是ip_err(),進行處理錯誤,如傳送icmp包。

linux核心的路由機制是可以實現靜態nat的(即是ip影射是靜態不變的)。其中,源位址的snat是通過動作為nat的策略來完成的,目的位址的dnat是通過型別為nat的路由項來完成的。

在ip_route_input_slow中,執行完fib_lookup後會有如下**:

u32 src_map = saddr;

if (res.r)

src_map = fib_rules_policy

(saddr, &res

, &flags

);if (res

.type

== rtn_nat)

key.src

= src_map;

首先,執行fib_rule_policy函式,將判斷如果剛才查策略表時查到的是動作為nat的策略,那麼將策略對應的影射源位址賦給src_map,最後會將這個src_map賦給key.src。這就記錄了snat的位址。

然後,if (res

.type

== rtn_nat) 判斷查路由表項的型別如果是nat,那麼將路由表項中的影射目的位址賦給key.dst,這就記錄了dnat的位址,然後用這個位址再呼叫fib_lookup函式查一遍影射後的目的位址的路由。

在下面更新快取的時候有如下**:

rth->rt_src_map = key

.src

;

rth->rt_dst_map = key

.dst;

這就把影射後的位址入到了快取結點中。

進而在執行ip_forward函式進行**時,有如下**:

if (rt->rt_flags & rtcf_nat

)

}
即如果是nat,執行ip_do_nat函式做nat,實際上就是根據skb-dst->rt_src_map和skb-dst->rt_dst_map做位址替換。

通過對kernel路由**的分析,使我加深了對作業系統特別是網路部分的理解。通過分析原始碼中的具體資料結構和演算法,對「程式=資料結構+演算法」這條簡單的公式有了更加深刻的理解。

載入linux kernel 的安全機制分析

system core mkbootimg mkbootimg.c 首先,一般bootimage 的結構如下 打包的時候會計算kernel,ramdisk分割槽資料的sha值,並把其存放在boot header 注意 計算的時候不包含boot header本身 sha init ctx sha up...

Linux Kernel時序的三種機制

最近在寫driver時,常常遇到需要 等待一段時間 再處理的動作,以往我都傻傻的用msleep 或mdelay 殊不知這種busy waiting會hold住cpu資源,在這段期間內都無法讓給其他process執行,時間短 10ms以下等級 或許還可以,太長就不行了,所以需要kernel本身就有提供...

SIP 路由機制

總的來說,sip中存在兩種路由場景 1,請求訊息的路由 2,響應訊息的路由 其中,響應訊息的路由非常簡單,就是完全依靠via來完成的,具體請見我關於rfc3261中會話流程的分析。下面我們只談sip請求訊息的路由。首先我們要搞清楚什麼是嚴格路由和鬆散路由。嚴格路由 strict routing 可以...