Linux Network Related Drive

cataloghtml

1. 經過套接字通訊
2. 網絡實現的分層模型
3. 網絡命名空間
4. 套接字緩衝區
5. 網絡訪問層
6. 網絡層
7. 傳輸層
8. 應用層
9. 內核內部的網絡通訊

 

1. 經過套接字通訊node

Linux的設計思想是"萬物皆文件",從開發者角度來看,外部設備在Linux(以及UNIX)中都是普通文件,經過正常的讀寫操做便可訪問,可是對於網卡而言,狀況會複雜的多。網卡的運做方式與普通的塊設備和字符設備徹底不一樣,一個緣由是(全部層次)使用了許多不一樣的通訊協議,爲創建鏈接須要指定許多選項,且沒法在打開設備文件時完成這些任務,所以,在/dev目錄下沒有與網卡對應的項
爲此,Linux採用的解決方案是將一種稱爲套接字的特殊結構用做到網絡實現的接口,這種方案如今已經成爲工業標準,POSIX標準中也定義了套接字linux

1. 套接字用於定義和創建網絡鏈接,以即可以用操做inode的普通方法(特別是讀寫操做)來訪問網絡
2. 從開發者角度,建立套接字的最終結果是一個文件描述符,它不只提供全部的標準函數,還包括幾個加強的函數
3. 用於實際數據交換的接口對全部的協議和地址族都是相同的
4. 在建立套接字時,不只要區分地址和協議族,還要區分基於流的通訊、基於數據報的通訊

0x1: 建立套接字數組

套接字不只能夠用於各類傳輸協議的IP鏈接,也能夠用於內核支持的全部其餘地址和協議類型(例如: IPX、Appletalk、本地UNIX套接字、DECNet、Netlink、以及在<socket.h>中列出的許多其餘類型)。所以,在建立套接字時,必須指定所須要的地址和協議類型的組合
須要注意的是,每一個地址族都只支持一個協議族(所以在建立socket的時候第三個參數經常爲0便可,即通知函數使用適當的默認協議),並且只能區分面向流的通訊和麪向數據報的通訊安全

0x2: 使用套接字cookie

0x3: 數據報套接字UDP是創建在IP鏈接之上的第二種大量使用的傳輸協議,UDP標識User Datagram Protocol(用戶數據報協議)網絡

Relevant Link:數據結構

http://www.cnblogs.com/LittleHann/p/3875451.html

 

2. 網絡實現的分層模型app

內核網絡子系統的實現和TCP/IP參考模型很是類似,相關的C語言代碼劃分爲不一樣層次,各層次都有明肯定義的任務,各個層次只能經過明肯定義的接口與上下緊鄰的層次通訊,這種作法的好處在於框架

1. 能夠組合使用各類設備、傳輸機制和協議
2. 一般的以太網卡不只可用於創建因特網(IP)鏈接,還能夠在其上傳輸其餘類型的協議,如Appletalk、IPX,而無須對網卡的設備驅動程序作任何類型的修改

 分層模型不只反映在網絡子系統的設計上,並且也反映在數據傳輸的方式上(對各層產生和傳輸的數據進行封裝的方式)

 

3. 網絡命名空間

內核的許多部分包含在命名空間中,經過命名空間能夠創建系統的多個虛擬視圖,而且彼此分隔開來,每一個實例看起來像是一臺運行Linux的獨立機器,可是在一臺物理機器上,能夠同時運行許多這樣的實例,從內核2.6.24開始,Linux內核也開始對網絡子系統採用命名空間,這對網絡子系統增長了一些額外的複雜性 

 

4. 套接字緩衝區

在內核分析(收到的)網絡分組時,底層協議的數據將傳遞到更高的層,發送數據時順序相反,各類協議產生的數據(首部和載荷)依次向更低的層傳遞,直至最終發送。這些操做的速度對網絡子系統的性能有決定性的影響,所以內核使用了一種特殊的結構,稱爲套接字緩衝區(socket buffer)
/source/include/linux/skbuff.h

struct sk_buff {
    /* These two members must be first. */
    struct sk_buff        *next;
    struct sk_buff        *prev;

    struct sock        *sk;
    ktime_t            tstamp;
    struct net_device    *dev;

    unsigned long        _skb_dst;
#ifdef CONFIG_XFRM
    struct    sec_path    *sp;
#endif
    /*
     * This is the control buffer. It is free to use for every
     * layer. Please put your private variables there. If you
     * want to keep them across layers you have to do a skb_clone()
     * first. This is owned by whoever has the skb queued ATM.
     */
    char            cb[48];

    unsigned int        len,
                data_len;
    __u16            mac_len,
                hdr_len;
    union {
        __wsum        csum;
        struct {
            __u16    csum_start;
            __u16    csum_offset;
        };
    };
    __u32            priority;
    kmemcheck_bitfield_begin(flags1);
    __u8            local_df:1,
                cloned:1,
                ip_summed:2,
                nohdr:1,
                nfctinfo:3;
    __u8            pkt_type:3,
                fclone:2,
                ipvs_property:1,
                peeked:1,
                nf_trace:1;
    __be16            protocol:16;
    kmemcheck_bitfield_end(flags1);

    void            (*destructor)(struct sk_buff *skb);
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
    struct nf_conntrack    *nfct;
    struct sk_buff        *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
    struct nf_bridge_info    *nf_bridge;
#endif

    int            iif;
#ifdef CONFIG_NET_SCHED
    __u16            tc_index;    /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
    __u16            tc_verd;    /* traffic control verdict */
#endif
#endif

    kmemcheck_bitfield_begin(flags2);
    __u16            queue_mapping:16;
#ifdef CONFIG_IPV6_NDISC_NODETYPE
    __u8            ndisc_nodetype:2;
#endif
    kmemcheck_bitfield_end(flags2);

    /* 0/14 bit hole */

#ifdef CONFIG_NET_DMA
    dma_cookie_t        dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
    __u32            secmark;
#endif

    __u32            mark;

    __u16            vlan_tci;

    sk_buff_data_t        transport_header;
    sk_buff_data_t        network_header;
    sk_buff_data_t        mac_header;
    /* These elements must be at the end, see alloc_skb() for details.  */
    sk_buff_data_t        tail;
    sk_buff_data_t        end;
    unsigned char        *head,
                *data;
    unsigned int        truesize;
    atomic_t        users;
};

套接字緩衝區用於在網絡實現的各個層次之間交換數據,而無須來回複製分組數據,對性能帶來了很大的提升。套接字結構是網絡子系統的基石之一,由於在產生和分析分組時,在各個協議層次上都須要處理該結構

0x1: 使用套接字緩衝區管理數據

套接字緩衝區經過其中包含的各類指針與一個內存區域相關聯,網絡分組的數據就位於該區域中,套接字緩衝區的基本思想是

經過操做指針來增刪協議首部

1. head和end指向數據在內存中的起始和結束位置: 這個區域可能大於實際須要的長度,由於在產生分組時,商不清楚分組的長度
2. data和tail指向協議數據區域的起始和結束位置
3. mac_header指向MAC協議首部的起始
4. network_header指向網絡層協議首部的起始
5. transport_header指向傳輸層協議首部的起始

這使得內核能夠將套接字緩衝區用於全部協議類型,正確地解釋數據須要作簡單的類型轉換,爲此內核提供了幾個輔助函數
data和tail使得在不一樣協議層之間傳遞數據時,無須顯式的複製操做

1. 在一個新分組產生時,TCP層首先在用戶空間中分配內存來容納該分組數據(首部和載荷),分配的空間大於數據實際須要的長度,所以較低的協議層能夠進一步增長首部
2. 分配一個套接字緩衝區,使得head和end分別指向新分配的較低層協議內存區的起始和結束地址,而TCP數據位於data和tail之間
3. 在套接字緩衝區傳遞到互聯網網絡層時,必須增長一個新層,只須要向已經分配但還沒有佔用的那部份內存空間寫入數據便可,除了data以外的全部指針都不變,data如今指向IP首部的起始處,再往下的各層會重複一樣的操做,直至分組完成,即將經過網絡發送
4. 對接收的分組進行分析的過程是相似的,分組數據複製到內核分配的一個內存區中,並在整個分析期間一直處於該內存區中,與該分組相關聯的套接字緩衝區在各層之間順序傳遞,各層依次將其中的各個指針設置爲正確值
//套接字緩衝區之因此可使用頭尾指針進行跨層傳遞處理的緣由在於,TCP/IP協議棧各層之間本質上是在進行逐層擴大的數據包裝,從內存空間視角看到的就是從棧頂逐漸向棧底發展,因此能夠經過預申請一塊大內存,在逐層封裝的時候經過移動頭尾指針實現協議層切換

套接字緩衝區須要不少指針來表示緩衝區中內容的不容部分,因爲網絡子系統必須保證較低的內存佔用和較高的處理速度,於是對於struct sk_buff來講,咱們須要保持該結構的長度儘量小

0x2: 管理套接字緩衝區數據

 

5. 網絡訪問層

網絡訪問層主要負責在計算機之間傳輸信息,與網卡的設備驅動程序直接協做

0x1: 網絡設備的表示

在內核中,每一個網絡設備都表示爲net_device結構的一個實例,在分配並填充該結構的一個實例以後,必須用net/core/dev.c中的register_netdev函數將其註冊到內核,該函數完成一些初始化任務,並將該設備註冊到通用設備機制內,這會建立一個sysfs項

[root@iZ23er0navtZ ~]# ll /sys/class/net
total 0
lrwxrwxrwx 1 root root 0 Dec  9 20:00 eth0 -> ../../devices/vif-0/net/eth0
lrwxrwxrwx 1 root root 0 Dec  9 20:00 eth1 -> ../../devices/vif-1/net/eth1
lrwxrwxrwx 1 root root 0 Dec  9 20:00 lo -> ../../devices/virtual/net/lo

0x2: 接收分組

分組到達內核的時間是不可預測的,全部現代的設備驅動程序都使用中斷來通知內核有分組到達,網絡驅動程序對特定於設備的中斷設置了一個處理例程,所以每當該中斷被觸發時(即分組到達),內核都會自動調用該處理例程,將數據從網卡傳輸到物理內存,或通知內核在必定時間後進行處理

幾乎全部的網卡都支持DMA模式,可以自行將數據傳輸到物理內存,但這些數據仍然須要解釋和處理,這會在稍後進行

1. 傳統方法

下面學習一個分組到達網絡適配器以後,該分組穿過內核到達網絡層函數的路徑

1. 分組是在中斷上下文中接收到的,因此處理例程只能執行一些基本的任務,避免系統(或當前CPU)的其餘任務延遲太長時間
2. net_interrupt是由設備驅動程序設置的中斷處理程序,它將肯定該中斷是否真的是由接收到的分組引起的,若是確實如此,則控制將轉移到net_rx
3. net_rx函數也是特定於網卡的
    1) 首先建立一個新的套接字緩衝區
    2) 分組的內容接下來從網卡傳輸到緩衝區(即進入物理內存)
    3) 而後使用內核源碼中針對各類傳輸類型的庫函數來分析首部數據,這項分析將肯定分組數據所使用的網絡層協議,例如IP協議
4. netif_rx函數不是特定於網絡驅動程序的,該函數位於net/core/dev.c,調用該函數,標誌着控制由特定於網卡的代碼轉移到了網絡層的通用接口部分。該函數的做用在於,將接收到的分組放置到一個特定於CPU的等待隊列上,並退出中斷上下文,使得CPU能夠執行其餘任務
5. 內核在全局定義的softnet_data數組中管理進出分組的等待隊列,數據項類型爲softnet_data

2. 對高速接口的支持

0x3: 發送分組

在網絡層中特定於協議的函數通知網絡訪問層處理由套接字緩衝區定義的一個分組時,將發送完成的分組

 

6. 網絡層

網絡訪問層仍然收到傳輸介質的性質以及相關適配器的設備驅動程序的很大影響,網絡層(具體地說是IP協議)與網絡適配器的硬件性質幾乎是徹底分離的
須要明白的是,硬件的性質是須要將較大的分組分隔爲較小的單位的首要緣由,由於每一種傳輸技術所支持的分組長度都有一個最大值,IP協議必須將較大的分組劃分爲較小的單位,由接收方從新組合,更高層協議不會注意到這一點,劃分後分組的長度取決於特定傳輸協議的能力

0x1: IPv4

ip_rcv函數是網絡層的入口點,分組向上穿過內核的路線以下

發送和接收操做的程序流程並不老是分離的,若是分組只經過當前計算機轉發,那麼發送和接收操做是交織的,這種分組不會傳遞到更高的協議層(或應用程序),而是當即離開計算機,發往新的目的地

0x2: 接收分組

1. 在分組(以及對應的套接字緩衝區,其中的指針已經設置了適當的值)轉發到ip_rcv以後,必須檢查接收到的信息,確保它是正確的
    1) 主要檢查計算的校驗和與首部中存儲的校驗和是否一致
    2) 其餘的檢查包括分組是否達到了IP首部的最小長度
    3) 以及分組的協議是否確實是IPv4(IPv6的接收例程是另外一個)
2. 在進行了這些檢查以後,內核並不當即繼續對分組的處理,而是調用一個netfilter掛鉤,使得用戶空間能夠對分組數據進行操做,netfilter掛鉤插入到內核源代碼中定義好的各個位置,使得分組可以被外部動態操做,掛鉤存在於網絡系統的各個位置,每種掛鉤都有一個特別的標記,例如: NF_IP_POST_ROUTING
3. 在內核到達一個掛鉤位置時,將在用戶空間調用對該標記支持的例程,接下來,在另外一個內核函數中繼續內核端的處理(分組可能在這個環節被修改)
4. 在下一步中,接收到的分組到達一個十字路口,此時須要判斷該分組的目的地是本地系統仍是遠程計算機。根據對分組目的地的判斷,須要將分組轉發到更高層、或轉到互聯網絡層的輸出路徑上
5. ip_route_input負責選擇路由,判斷路由的結果是,選擇一個函數,進行進一步的分組結果,可用的函數是
    1) ip_local_deliver: 分組是交付到本地計算機下一個更高層的
    2) ip_forward: 分組是轉發到網絡中的另外一臺計算機

0x3: 交付到本地傳輸層

若是分組的目的地是本地計算機,ip_local_deliver必須設法找到一個適當的傳輸層函數,將分組轉送過去,IP分組一般對應的傳輸層協議是TCP或UDP

0x4: 分組轉發

IP分組可能如上所述交付給本地計算機處理,它們也可能離開互聯網絡層,轉發到另外一臺計算機,而不牽涉本地計算機的高層協議實例,分組的目標地址可分爲如下兩類

1. 目標計算機在某個本地網絡中,發送計算機與該網絡有鏈接
2. 目標計算機在地理上屬於遠程計算機,不鏈接到本地網絡,只能經過網關訪問

該信息由路由表(routing table)提供,路由表由內核經過多種數據結構實現並管理。在接收分組時調用的ip_route_input函數充當路由實現的接口,這一方面是由於該函數可以識別出分組是交付到本地仍是轉發出去,另外一方面是該函數可以找到通向目標地址的路由。目標地址存儲在套接字緩衝區的dst字段中

0x5: 發送分組

內核提供了幾個經過互聯網絡層發送數據的函數,可由較高協議層使用,其中ip_queue_xmit是最常使用的一個

0x6: netfilter

netfilter是一個Linux內核框架,使得能夠根據動態定義的條件來過濾和操做分組,這顯著增長了可能的網絡選項的數目,從簡單的防火牆,到對網絡通訊數據的詳細分析,甚至更復雜的依賴於狀態的分組過濾器

1. 擴展網絡功能

1. 根據狀態及其條件,對不一樣數據流方向(進入、外出、轉發)進行分組過濾(packet filtering)
2. NAT(network address translation 網絡地址轉換)根據某些規則來轉換源地址和目標地址(一般用於IP假裝或透明代理)
3. 分組處理(packet manghing)和操做(manipulation),根據特定的規則拆分和修改分組

能夠經過在運行期間動態向內核載入模塊來加強netfilter功能,一個定義好的規則集,告知內核在什麼時候使用各個模塊的代碼。內核和netfilter之間的接口保持在很小的規則上,儘量使兩個領域彼此隔離,避免兩者的互相干擾並改進網絡代碼的穩定性
netfilter掛鉤位於內核中的各個位置,以支持netfilter代碼的執行,這些不只用於IPv4,也用於IPv6和DECNET協議,netfilter實現劃分爲以下幾個部分

1. 內核代碼中的掛鉤,位於網絡實現的核心,用於調用netfilter代碼
2. netfilter模塊,其代碼掛鉤內部調用,但其獨立於其他的網絡代碼,一組標準模塊提供了經常使用的函數,但能夠在擴展模塊中定義用戶相關的函數

2. 調用掛鉤函數

在經過掛鉤執行netfilter代碼時,網絡層的函數將會被中斷。掛鉤的一個重要特性是,它們將一個函數劃分爲兩部分

1. 前一部分在netfilter代碼調用前運行
2. 後一部分在netfilter代碼調用後運行

3. 掃描掛鉤表

若是至少註冊了一個掛鉤函數並須要調用,那麼會調用nf_hook_slow,全部掛鉤都保存在二維數組nf_hooks中

4. 激活掛鉤函數

每一個hook函數都返回下列值之一

1. NF_ACCEPT: 表示接受分組,這意味着所述例程沒有修改數據,內核將繼續使用未修改的分組,使之穿過網絡實現中剩餘的協議層(或經過後續的掛鉤)
2. NF_STOLEN: 表示掛鉤函數"竊取"了一個分組並處理該分組,此時,該分組已經與內核無關,沒必要再調用其餘掛鉤,還必須取消其餘協議層的處理
3. NF_DROP: 經過內核丟棄該分組,如同NF_STOLEN,其餘掛鉤或網絡層的處理都再也不須要了,套接字緩衝區(和分組)佔用的內存空間能夠釋放,由於其中包含的數據能夠被丟棄,例如掛鉤可能認定分組是損壞的
4. NF_QUEUE: 將分組置於一個等待隊列上,以便其數據能夠由用戶空間代碼處理,不會執行其餘掛鉤函數
5. NF_REPEAT: 表示再次調用該掛鉤

除非全部掛鉤函數都返回NF_ACCEPT(NF_REPEAT不是最終結果),不然分組不會在網絡子系統進一步處理,全部其餘的分組,不是被丟棄,就是由netfilter子系統處理
內核提供了一個掛鉤函數的集合,使得沒必要爲每一個場合都單獨定義掛鉤函數,這些稱爲iptables,用於分組的高層處理,它們使用用戶空間工具iptables配置
0x7: IPv6

 

7. 傳輸層

兩個基於IP的主要傳輸協議分別是UDP和TCP,前者用於發送數據報,後者可創建安全的、面向鏈接的服務

0x1: UDP

0x2: TCP

TCP提供的函數比UDP多得多,所以在其內核中的實現要困可貴多,也牽涉更廣的內容。TCP鏈接老是處於某個明肯定義的狀態,各個狀態之間遵循必定的規範進行遷移

 

8. 應用層

套接字將UNIX隱喻"萬物皆文件"應用到了網絡鏈接上,內核與用戶空間套接字之間的接口實如今C標準庫中,使用了socketcall系統調用(老的Linux內核採用多路複用方式,新內核已經將多路複用拆解到了單獨的系統調用中了)
Linux採用了內核套接字的概念,使得與用戶空間中的套接字的通訊儘量簡單,對程序使用的每一個套接字來講,都對應於

1. socket結構實例: 充當向下(到內核)
2. sock結構實例: 充當向上(到用戶空間)接口

0x1: socekt數據結構

0x2: 套接字和文件

在鏈接創建後,用戶空間進程使用普通的文件操做來訪問套接字,因爲VFS層的開放結構,內核只須要不多的工做。在VFS虛擬文件系統的VFS inode中,每一個套接字都分配了一個該類型的inode,inode又關聯到另外一個與普通文件相關的結構,用於操做文件的函數保存在一個單獨的指針表中

<fs.h>
struct inode
{
    ..
    struct file_operations *i_fop;
    ..
}

所以,對套接字文件描述符的文件操做,能夠透明地重定向到網絡子系統的代碼

 

9. 內核內部的網絡通訊

與其餘主機通訊,不僅是用戶層應用程序的需求,內核一樣須要與其餘計算機通訊,例如網絡文件系統如CIFS或NCPFS都依賴於內核內部提供的網絡通訊支持。同時,內核各組件之間也須要進行通訊,以及用戶層和內核之間的通訊,netlink機制提供了所需的框架

0x1: 通訊函數

0x2: netlink機制

netlink是一種基於網絡的機制,容許在內核內部以及內核與用戶層之間進行通訊,它的思想是,基於BSD的網絡套接字使用網絡框架在內核和用戶層之間進行通訊,但netlink套接字大大擴展了可能的用途,該機制不只僅用於網絡通訊,其更重要的用戶是對象模型,它使用netlink套接字將各類關於內核事務的狀態信息傳遞到用戶層,其中包括新設備的註冊和移除、硬件層次上發生的特別事件等
內核中還有其餘一些可選的方法可以實現相似的功能,例如procfs或sysfs中的文件,但與這些方法相比,netlink機制有一些很明顯的優點

1. 任何一方都不須要輪詢,若是經過文件傳遞狀態信息,那麼用戶層須要不斷檢查是否有新消息到達
2. 系統調用和ioctl也可以從用戶層向內核傳遞信息,但比簡單的netlink鏈接更難於實現
3. 使用netlink不會與模塊有任何衝突,但模塊和系統調用顯然配合的不是很好
4. 內核能夠直接向用戶層發送信息,而無須用戶層事先請求,使用文件也能夠作到,但系統調用和ioctl是不可能的
5. 除了標準的套接字,用戶空間應用程序不須要使用其餘東西來與內核交互

netlink只支持數據報信息,但提供了雙向通訊,另外,netlink不只支持單播消息,也能夠進行多播,相似於其餘套接字的機制,netlink的工做方式是異步的

Relevant Link:

 

Copyright (c) 2015 LittleHann All rights reserved

相關文章
相關標籤/搜索