ip 報文在內核處理的實現

ip 報文在內核處理的實現

這篇文章解釋了ip報文在內核裏面的實現,我們會根據報文穿過 ip協議協議層來介紹ip的基本屬性。 爲了確保我們的解釋清晰易理解,我們假定這是一個普通的ip報文沒有特殊屬性。
所有關於ip的特殊屬性, 例如 分片和整形, 源路由,組播,等等都會在下一個章節介紹。

一個IP 報文以三種方式進入ip協議棧

  • 報文通過網絡適配器存儲在對應CPU的輸入 隊列裏面, 一旦報文在數據鏈路層被確認爲三層報文 協議類型
    ETH_PROTO_IP , 報文被傳輸到ip_rcv() 函數。

  • ip報文的第二個入口位於傳輸協議的接口, 那些報文使用了TCP UDP 和其他一些使用的IP協議的報文。
    它們使用 ip_queue_xmit() 接口去包裝一個傳輸層PDU 到 IP報文然後去發送它,其他接口可以生成IP報文
    在傳輸層的下層。

  • 第三種方式,IP協議層生成ip報文。 主要是多播報文,巨型幀的分片產生的新報文,還有不包括特殊負載的
    ICMP或者 IGMP報文,這種報文用的特殊方式創建 例如 icmp_send()

一旦一個報文進入IP協議層,有幾種方式處理。我們通常區分計算機在Internet協議方面可以承擔的兩個不同角色,其中第一種情況是第二種情況的特殊情況:

  • 終端系統: Linux計算機通常被配置爲終端系統—它被用作工作站或服務器,
    主要承擔運行用戶應用程序或提供應用程序服務的任務。
    此外,Web服務器和網絡打印機只不過是終端系統(關於IP層)。終端系統的基本特性是不轉發IP數據包。
    這意味着您可以通過終端系統只有一個網絡適配器這一事實輕鬆地識別它。
    即使一個系統有幾個網絡訪問可以配置爲一個主機,如果包轉發是禁用的

  • 路由器: 路由器將到達網絡適配器的IP數據包傳遞給第二個網絡適配器。
    這意味着路由器有幾個網絡適配器,可以在這些接口之間轉發數據包。
    當信息包到達路由器時,通常有兩種選擇:它們可以在本地傳遞信息包(例如,它們可以在本地傳遞信息包)。
    將它們發送到傳輸層)或轉發它們。第一種情況與包到達終端系統的過程相同,其中包總是在本地交付。
    因此,路由器可以被認爲是終端系統的泛化,具有額外的功能

Linux允許您在運行時啓用和禁用包轉發機制,前提是在創建內核時集成了對轉發的支持。
目錄/proc/sys/net/ipv4/包含一個虛擬文件ip_forward。
有一種方法可以從proc目錄中更改系統設置。如果將0寫入此文件,則禁用包轉發。
要激活IP包轉發,可以使用命令echo ‘1’ > /proc/sys/net/ipv4/ip_forward。

下圖顯示一個IP包在Linux中通過Internet協議實現的路徑。
灰色橢圓表示被調用的函數,矩形表示netfilter鉤子在Internet協議中的位置。

Linux中Internet協議實現的體系結構
在這裏插入圖片描述
下面介紹ip報文在內核裏面實現的不同方式,我們首先介紹接收的報文然後做轉發或者移送本機,下一章節
介紹報文怎麼從傳輸層到ip層。

傳入ip包的路徑

進入封包到第3層邊界的路徑。一旦NET_RX微線程從輸入隊列中刪除了一個包,netif_rx_action()將選擇適當的第3層協議。
接下來,選擇Internet協議,並根據以太網協議字段(ETH_PROTO_IP)中的標識符或其他MAC傳輸協議的適當字段調用ip_rcv()函數。

ip_rcv() net/ipv4/ip_input.c
ip_rcv(skb、dev、pkt_type)爲IP協議做一些工作。
首先,該函數拒絕未發送到本地計算機的數據包。
例如,混雜模式允許網絡設備接受實際發送到另一臺計算機的數據包。
在較低的層中,這樣的包被包類型(skb->pkt_type PACKET_OTHERHOST)過濾。

隨後,檢查數據包的基本正確性準則:

  • 報文長度是否大於 IP報文頭部?

  • 是不是 ipv4?

  • 檢查checksum是否正確?

  • 報文長度是否不對?

如果實際的包大小與套接字緩衝區(skb->len)中維護的信息不匹配,則當前的包數據範圍由skb_trim(skb, iph->total_len)調整。
(參見4.1節)。既然包是正確的,就調用netfilter鉤子NF_IP_PRE_ROUTING。Netfilter允許您根據需要通過特定的函數擴展各種協議的過程。
Netfilter鉤子總是駐留在某些協議的策略點上,例如用於防火牆、QoS和地址轉換功能。這些例子將在後面的章節中討論。
netfilter鉤子由宏調用,處理netfilter擴展後的函數以函數指針的形式傳遞給這個宏。如果未配置netfilter,則該宏將確保直接跳轉到此後續函數。
在圖14-4中,我們可以看到這個過程繼續使用ip_rcv_finish(skb)。

ip_rcv_finish() net/ipv4/ip_input.c
ip_rcv_finish(skb)中調用ip_route_input()函數來確定包的路由。
套接字緩衝區的skb->dst指針被設置爲路由緩存中的一個條目,該條目不僅存儲在IP層上的目標,而且如果存在,還存儲到硬標頭緩存中的一個條目(用於第2層幀包標頭的緩存)。
如果ip_route_input()找不到路由,則丟棄數據包。

在下一步中,ip_rcv_finish()檢查IP包頭是否包含選項。如果是這種情況,則分析選項,並創建ip_options結構。所有選項集都以有效的形式存儲在這個結構中。
第14.3節描述瞭如何處理IP選項。

最後,在ip_rcv_finish()中,IP協議的過程到達了發送給本地計算機的數據包和要轉發的數據包之間的連接點。關於IP包的進一步路徑的信息存儲在路由條目skb->dst中。注意,這裏使用了Linux內核中經常使用的一個技巧。
如果使用一個開關(變量值)來選擇不同的函數,那麼我們只需向每個函數插入一個指針。這爲我們節省了關於程序應該如何繼續的每個決定的if或switch指令。在這裏使用的例子中,指針skb->dst->input()指向的函數應該被用來進一步處理一個包:

  • ip_local_deliver()是在應該發送到本地計算機的單播和多播包的情況下輸入的。

  • ip_forward()處理應該轉發的所有單播包。

  • ip_mr_input()用於應該轉發的多播包。

從上面的討論中我們可以看出,包可以採用不同的路徑。下一節描述如何處理要轉發的包(skb->dst->輸入= ip_forward)。隨後,我們將看到skb->dst->input = ip_local_deliver如何處理要在本地傳遞的包

轉發報文

如果一臺計算機有幾個網絡適配器,並且啓用了包IP轉發(/proc/sys/net/ipv4/ip_forward 1),那麼發送給其他計算機的包由ip_forward()函數處理。這個函數完成轉發數據包所需的所有工作。
最重要的任務路由已經在ip_input()中完成,因爲必須能夠發現包是在本地交付還是必須被轉發

ip_forward() net/ipv4/ip_forward.c

ip_forward(skb)的主要任務是處理Internet協議的一些條件(例如包的生存期)和包選項。首先,刪除未使用pkt_type == PACKET_HOST標記的包。接下來,檢查包的覆蓋範圍。如果它的TTL字段的值是1(在它遞減之前),那麼這個包就被刪除了。
RFC 791規定,如果發生這樣的操作,必須將ICMP包返回給發送方,以通知發送方(ICMP_TIME_EXCEEDED).

一旦檢查了重定向消息(如果適用),就會檢查套接字緩衝區,以查看是否有足夠的內存供headroom使用。這意味着函數skb_cow(skb, headroom)用於檢查在輸出網絡設備(out_dev->hard_header_len)中是否仍然有足夠的空間放置MAC頭文件。
如果不是這樣,那麼skb_realloc_headroom()將創建足夠的空間。隨後,IP包的TTL字段減少1。

當實際的包長度(包括MAC報頭)已知時,將檢查它是否真的適合新的輸出網絡設備的幀格式。如果太長(skb->len > mtu),並且由於在IP報頭中設置了Don’t-Fragment位,因此不允許分段,則數據包被丟棄,ICMP消息ICMP_FRAG_NEEDED被髮送給發送方。無論如何,數據包還沒有被分割;破碎是延遲。
對這種情況的早期測試只會阻止潛在的候選Don’t-Fragment運行整個IP協議處理過程

ip_forward_finish( ) net/ipv4/ip_forward.c

在圖14-4中,我們可以看到ip_forward()函數被一個netfilter鉤子分成了兩部分。一旦NF_IP_FORWARD鉤子被處理,這個過程將繼續使用ip_forward_finish()。
這個函數實際上只有很少的功能(除非啓用了FASTROUTE)。一旦在ip_forward_options()中處理了IP選項(如果使用了這些選項),就會調用ip_send()函數來檢查包是否必須分段,並最終執行分段(如果適用的話)。(見部分14.2.3。)

ip_send() include/net/ip.h

ip_send(skb)決定包是否應該立即傳遞給ip_finish_output(),還是ip_fragment()首先將其調整爲合適的第2層幀大小。(見部分14.2.3。)

ip_finish_output() net/ipv4/ip_output.c

ip_finish_output(skb)啓動Internet協議的最後一個任務。首先,將skb->dev指針設置爲輸出網絡設備dev,將第2層數據包類型設置爲ETH_P_IP。隨後,處理netfilter鉤子NF_IP_POST_ROUTING。netfilter的確切操作和Internet協議中不同的連接點集在第19.3節中進行了描述。
netfilter鉤子通常在調用後繼續使用內聯函數ip_finish_output2()。

ip_finish_output2() net/ipv4/ip_output.c

此時,數據包離開Internet協議,如果需要,使用地址解析協議(ARP)。第15章描述了地址解析協議。就目前而言,理解以下內容就足夠了:

  • 如果使用的路由條目(skb->dst)已經包含了對第2層報頭緩存(dst->hh)的引用,那麼第2層的包報頭將直接複製到IP包報頭前面的socket緩衝區的包數據空間中。這裏使用的output()函數是dev_queue_xmit(),如果硬件標頭緩存中的條目有效,就會調用它。dev_queue_xmit()確保socket緩衝區立即通過網絡設備dev發送。

  • 如果在硬標頭緩存中還沒有條目,則調用相應的地址解析例程,該例程通常是函數_resolve_output()。

上面描述的過程經過了優化,因此包可以快速通過路由器,而不需要特殊的選項。然而,在與相應的處理例程(例如,netfilter、多播、ICMP、分段或IP包選項)相連接的地方變得很明顯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章