天天看點

Linux協定棧(7)——網絡層實作

網絡層具體就是IP協定層,處理發送和接收資料外,還需要進行轉發和路由分組。在查找最佳路由并選擇适當網卡的時候也會涉及對底層位址族的處理,例如MAC位址。

ip頭資料結構定義在:include/uapi/linux/ip.h。

struct iphdr {

#if defined(__LITTLE_ENDIAN_BITFIELD)

        __u8   

ihl:4,

                version:4;

#elif defined (__BIG_ENDIAN_BITFIELD)

        __u8    version:4,

                ihl:4;

#else

#error  "Please fix <asm/byteorder.h>"

#endif

        __u8    tos;

        __be16  tot_len;

        __be16  id;

        __be16 

frag_off;

        __u8    ttl;

        __u8    protocol;

        __sum16 check;

        __be32  saddr;

        __be32  daddr;

        /*The options start here. */

};

            ipv4資料包的主接收方法是ip_rcv()函數,會檢測報到類似如果是PACKET_OTHERHOST(定義在include/uapi/linux/if_packet.h檔案中)則直接丢棄。檢查是否是共享的包,如果是分享的包則克隆它。擷取ip頭資料結構,來操作相關協定事務。然後調用NK_HOOK,NF_HOOK定義在include/linux/netfilter.h檔案中。

static inline int

NF_HOOK(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, struct sk_buff *skb,

        struct net_device *in, struct net_device *out,

int (*okfn)(struct net *, struct sock *, struct sk_buff *))

            它是netfilter鈎子函數,如果允許包傳遞則傳回1。如果傳回其他值說明這個包被hook給消耗掉了。

            其中hook為NF_INET_PRE_ROUTING(定義在

<b>include/uapi/linux/netfilter.h</b>)

okfn指向ip_rcv_finish()函數。ip_rcv_finish()也定義在:<b>net/ipv4/ip_input.c</b>

            在ip網絡層中需要分段、重組的實作。須在路由選在子系統中查找,确定是發給目前主機還是轉發。

            如果是目前主機則依次調用方法ip_local_deliver()和ip_local_deliver_finish()函數。如果需要轉發則調用ip_forward()函數。

IP分組可能是分片的,可以通過ip_defrag重新組合分片分組的各個部分。代碼流程如下圖(圖摘自《深入linux核心架構》):

Linux協定棧(7)——網絡層實作

            核心在獨立的緩存中管理原本屬于一個分組的各個分片,該緩存稱為分片緩存(fragment cache)。屬于同一個分組的各個分片儲存在一個獨立的等待隊列中,直至該分組的所有分片到達。

            ip_frag_reasm将各個分片重新組合起來。

如果資料是給本機的,那麼傳回到ip_local_deliver。調用netfilter挂鈎NF_IP_LOCAL_IN恢複在ip_local_deliver_finish函數中的處理。

            分組的協定辨別符确定一個傳輸層的函數,分組将傳遞給該函數。每個協定都有一個net_protocol結構的執行個體。定義在:

<b>include/net/protocol.h</b>檔案中

struct net_protocol {   

        int                     (*early_demux)(struct sk_buff *skb);

        int                     (*early_demux_handler)(struct sk_buff *skb);

        int                     (*handler)(struct sk_buff *skb);

        void                    (*err_handler)(struct sk_buff *skb,

u32 info);                                     

        unsigned int           

no_policy:1,             

                                netns_ok:1,              

                                /* does the protocol do more stringent

                                 *

icmp tag validation than simple

socket lookup?

                                 */                      

icmp_strict_tag_validation:1;                                                      

ip分組也可能轉發到另一台計算機。這就需要調用ip_forward函數。

            ip_forward使用NF_HOOK挂鈎函數,挂鈎編号為NF_INET_FORWARD,回調函數為ip_forward_finish。

由ip_queue_xmit函數将資料包從L4移到L3

  網絡層要選擇合适的網間路由和交換結點,確定資料及時傳送。其主要任務包括 (1)路由處理,即選擇下一跳 (2)添加 IP header(3)計算 IP header

checksum,用于檢測 IP 封包頭部在傳播過程中是否出錯

(4)可能的話,進行 IP 分片(5)處理完畢,擷取下一跳的 MAC 位址,設定鍊路層封包頭,然後轉傳入連結路層處理。

            發送和接收操作的流程并不總是分離的,如果分組隻通過目前計算機轉發,那麼發送和接收操作是交織的。

繼續閱讀