日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

深入理解Linux网络技术内幕学习笔记第十九章:因特网协议第四版(IPv4):Linux的原理和功能

發(fā)布時間:2023/12/8 linux 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入理解Linux网络技术内幕学习笔记第十九章:因特网协议第四版(IPv4):Linux的原理和功能 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本章主要介紹Linux支持IP的數(shù)據(jù)結構和基本活動,如入口IP包如何傳遞至IP接收函數(shù),校驗和如何驗證,以及IP選項如何處理。

主要的IPv4數(shù)據(jù)結構:

struct iphdr{

?? ?

};//ip報頭

struct ip_options{

};//此結構代表必須被傳輸或轉發(fā)的封包選項,那些選項存儲在此結構中,而不是struct iphdr結構中

struct ipcm_cookie{

?? ?

};//此結構包含了傳輸封包所需的各種信息

struct ipq{

?? ?

};//IP封包的片段集,參見第二十二章IP片段hash表的組織一節(jié)

struct inet_peer{

?? ?

};//內(nèi)核會為最近連接過的每個遠程主機都保留一個這一結構的實例

struct ipstats_mib{

?? ?

};//SNMP(簡單網(wǎng)絡管理協(xié)議)采用一種名為MIB(Management Information Base ,管理信息庫)的對象來收集系統(tǒng)的相關統(tǒng)計數(shù)據(jù)。此結構會保存關于IP層的統(tǒng)計資料。

struct in_device{

?? ?

};//此結構存儲了一個網(wǎng)絡設備所有與IPv4相關的配置內(nèi)容。net_device結構中的ip_ptr指針指向該結構

struct in_ifaddr{

?? ?

};//當在接口山配置一個IPv4地址時,內(nèi)核會建立一個in_faddr結構

struct ipv4_devconf{

?? ?

};//該結構用于調(diào)整網(wǎng)絡設備的行為。每個設備都有一個實例。其字段通過/proc/sys/net/ipv4/conf輸出

struct ipv4_config{

?? ?

};//不同于ipv4_devconf存儲每個設備的配置,該結構存儲每個主機的配置

struct cork{

?? ?

};//該結構用于存儲套接字選項CORK選項。第二十一章會看到其字段如何應用

sk_buff和net_device結構里與校驗和有關的字段:

net_device->features字段表明設備的能力,其中和控制檢驗和計算的一些標志如下:

? ? NETIF_F_NO_SUM:此設備很可靠,不需要使用任何L4校驗和?;乩@設備就開啟了此c功能。

? ? NETIF_F_IP_CSUM:此設備可以在硬件中計算L4檢驗和,但是只針對使用IPv4的TCP,UDP。

? ? NETIF_F_HW_CSUM:此設備可以為任何協(xié)議在硬件中計算L4校驗和。

skb_buff中主要有兩個字段跟校驗和有關:skb->csum和skb->ip_summed。

當一個封包被接收時,skb->csum可能包含其L4校驗和,skb->ip_summed字段則會記錄L4校驗和的狀態(tài),這些狀態(tài)代表設備驅(qū)動程序要告訴L4層的事,狀態(tài)有下列這些值:

? ? CHECKSUM_NONE:csum中的校驗和無效,需要L4層自己來計算。為社么csum中的校驗和無效,因為①設備不提供硬件校驗和計算。②校驗和必須重新計算并驗證。如第十八章“對L4校驗和所做的修改”一節(jié)提到的情況。

? ? CHECKSUM_HW:NIC以L4報頭和有效載荷計算了校驗和,然后把校驗和拷貝到skb->csum字段。軟件(L4接收函數(shù))需要把偽報頭的校驗和添加到skb->csum,并驗證最后所得的校驗和。

? ? CHECKSUM_UNNECESSARY:NIC已經(jīng)計算了L4報頭以及偽報頭的校驗和(偽報頭的校驗和可以由設備驅(qū)動程序在軟件中計算),所以,軟件(L4接收函數(shù))無需再計算L4的校驗和。

當一個封包被傳輸時,skb->csum不再是校驗和本是,而是指向NIC要把它計算的校驗和即將存放的地方,也就是說,封包傳輸期間,只有當校驗和是在硬件中計算時,才會用到此字段。skb->ip_summed依然表示L4校驗和狀態(tài):

?? ?CHECKSUM_NONE:協(xié)議已經(jīng)處理了校驗和,設備不需要做任何事。

?? ?CHECKSUM_HW:協(xié)議只把偽報頭的校驗和存儲在報頭中,設備應該添加L4報頭和有效載荷的校驗和。

封包的一般性處理:

協(xié)議初始化:

ipv4協(xié)議由ip_init函數(shù)初始化,該函數(shù)完成以下主要任務:

? ? 為ip封包注冊處理函數(shù)ip_rcv。(參見第十三章)

? ? 初始化路由子系統(tǒng),包括與協(xié)議無關的緩存。(參見第三十二章)

? ? 初始化用于管理ip端點的基礎架構(參見二十三章“長效ip端點信息”一節(jié))

開機期間,ip_init會由inet_init調(diào)用。inet_init會處理所有與ipv4有關的子系統(tǒng)的初始化。

和Netfilter交互:

基本上,防火墻在網(wǎng)絡堆棧程序中的某些地方都有鉤子函數(shù)。當符合某些條件時,封包就會通過那些鉤子函數(shù)。

與路由子系統(tǒng)的交互:

ip層在好幾個地方必須和路由表交互。本章只簡單說明ip層用于查詢路由表的三個函數(shù):

? ? ip_route_input:決定封包是被本地傳遞,轉發(fā)或丟棄。

? ? ip_route_output_flow:傳輸封包前使用,此函數(shù)會返回下個跳點以及要使用的出口設備。

? ? dst_pmtu:給定一個路由表緩存項目,返回相關的PMTU。

上述函數(shù)會把路由表的查詢結構存儲在skb->dst中。

處理輸入ip封包:

下面從ip_rcv函數(shù)開始分析內(nèi)核網(wǎng)絡協(xié)議棧內(nèi)ip封包的路徑。ip_rcv函數(shù)原型如下:

int ip_rcv(struct sk_buff *skb,struct net_device *dev,struct packet_type *pt);

在第十章和第十三章已經(jīng)知道,NIC驅(qū)動如何設定L3協(xié)議標識符skb->protocol和封包類型skb->pkt_type。當L2的目的地址和接收接口的地址不同時,skb->pkt_type = PACKET_OTHERHOST,通常這些包會被拋棄去,若接口處于混雜模式,會把這些包傳給相關的嗅探器。但是,到了L3,ip_rcv只會將其丟棄:

? ? if(skb->pkt_type == PACKET_OTHERHOST)

?? ?? ? goto drop;

skb_share_check會檢查封包的引用計數(shù)是否大于1,若處理程序看見引用計數(shù)大于1,就會自己建立一份緩沖區(qū)副本,使其可以修改封包。

? ? if((skb = skb_share_check(skb,GFP_ATOMIC)) == NULL){

?? ?? ? IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);

?? ?? ? goto out;

?? ?}

pskb_may_pull的工作是確保skb->data所指區(qū)域包含的數(shù)據(jù)至少和ip頭一樣大。如果條件符合,則無事可做,否則缺漏的部分就會從skb_shinfo(skb)->frags[]里的數(shù)據(jù)片段(如果有的話)拷貝過來 ,之后再次初始話iph(struct iphdr)。

? ? if(!pskb_may_pull(skb,sizeof(struct iphdr)))

?? ?? ? goto inhdr_error;

? ? iphdr = skb->nh.iph;

接著對ip報頭做一些健康檢查。

? ? if(iph->ihl < 5 || iph->version !=4)

?? ?? ? goto inhdr_error;

現(xiàn)在,重復先前做過的檢查,但是這次是完整的ip頭(包括選項)。

? ? if(!pskb_may_pull(skb,iph->ihl*4))

?? ?? ? goto inhdr_error;

? ? iph = skb->nh.iph;

接著計算校驗和。

? ? if(ip_fast_csum((u8 *)iph,iph->ihl) != 0)

?? ?? ? goto inhdr_error;

然后繼續(xù)檢查,確保接收的封包長度大于或等于ip報頭中記錄的長度(因為L2層可能為了滿足最小傳輸尺寸而做了填充,所以封包長度可能大于ip頭中記錄的長度)。同時確保封包的此少和ip報頭一樣大。

? ? {

?? ?? ? _ _u32 len = ntohs(iph->tot_len);

?? ?? ? if(skb->len < len || len < iph->ihl<<2))//<<2是因為報頭的大小以4字節(jié)為單位,所以需要先乘以4

?? ??? ?? ? goto inhdr_error;

//pskb_trim_rcsum用于檢查L2是否填充了封包使其達到特定的最小長度,如果有,將其剪裁成正確的大小

?? ?? ? if(pskb_trim_rcsum(skb,len)){

?? ??? ??? ?IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);

?? ??? ?? ? goto drop;

?? ??? ?}

?? ?}

最后來到函數(shù)的尾端,調(diào)用Netfilter子系統(tǒng)。

? ? return NF_HOOL(PF_INET,NF_IP_PRE_ROUTING,? ? skb , dev ,NULL,ip_rcv_finish);

Netfilter子系統(tǒng)(確切的說,是PF_INET,NF_IP_PRE_ROUTING位置)不決定丟棄封包,則后續(xù)會執(zhí)行ip_rcv_finish函數(shù)。

ip_rcv_finish函數(shù)(static inline? int ip_rcv_finish(struct sk_buff *skb) ):

ip_rcv主要做一些基本的健康檢查,ip_rcv_finish會處理主要工作:

? ? 決定封包傳給本地還是轉發(fā),若轉發(fā),還要找到出口設備和下個跳點。

? ? 分析和處理一些ip選項,并非所有選項都在這處理。

skb->nh字段是在netif_receive_skb里初始化的,當時,還不知道L3協(xié)議,所以用nh.raw初始化,現(xiàn)在,可以取得指向IP報頭的指針了。

? ? struct net_device *dev = skb->dev;

? ? struct iphdr *iph = skb->nh.iph;

skb->dst可能包含封包通往其目的的路由信息,如果沒有,詢問路由子系統(tǒng)。

? ? if(skb->dst == NULL){

?? ?? ? if(ip_route_input(skb,iph->daddr,iph->saddr,iph->tos,dev))

?? ??? ??? ?? ? goto drop;

?? ?}

接著,更新Traffic Control(Qos層)所用的統(tǒng)計數(shù)據(jù)。

? ? #ifdef CONFIG_NET_CLS_ROUTE

?? ??...

? ? #endif

當ip報頭的長度大于20字節(jié)時,有一些IP選項要處理。

? ? if(iph->ipl > 5){

?? ?? ? struct ip_options *opt;

? ? ? ? if(skb_cow(skb,skb_headroom(skb))){//skb_cow,如果緩沖區(qū)和別人共享,就會做出緩沖區(qū)副本

?? ??? ?? ? IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);

?? ??? ?? ? goto drop;

?? ??? ?}

?? ?? ? iph = skb->nh.iph;

//解析報頭中的IP選項,將分析結果放在中skb->cb所指的私有數(shù)據(jù)域字段的ip_option結構內(nèi)。

?? ?? ? if(ip_options_compile(NULL,skb))

?? ??? ?? ? goto inhdr_error;

?? ?}

在封包是來源地路由的情況下,內(nèi)核必須檢查該設備的配置是否允許使用該選項。一般情況下,默認是允許的。若設備配置不允許來源地路由選項,則該封包就被丟棄(但不會產(chǎn)生ICMP消息)。當設備允許IP源路由時,調(diào)用ip_options_rcv_srr函數(shù)設置skb->dst,決定使用哪個設備把該封包轉發(fā)至來源地路由列表中的下一個跳點。

? ? if(opt->srr){

?? ?? ? ....

?? ?? ? if(ip_options_rcv_srr(skb))

?? ??? ?? ? goto drop;

?? ?}

ip_rcv_finish函數(shù)最后會調(diào)用dst_input,完成封包的處理。

IP選項處理:

并非一個封包的所有ip選項都必須在其所有片段中重復,下面是選項相關的主要API:

? ? ip_options_compile:分析IP報頭中的一群選項,然后對一個ip_options結構的實例初始化。

? ? ip_options_build:對IP報頭中選項部分做初始化,傳輸本地封包時會用到該函數(shù)。

? ? ip_options_fragment:第一個片段時唯一繼承了原有封包所有選項的片段,其他片段則不會,但是會以空選項填充,使所有片段尺寸一樣,這樣可以簡化分片流程。

? ? ip_forward_options:轉發(fā)一個封包時,有些選項必須被處理。

? ? ip_options_get:此函數(shù)會接收一群選項,用ip_options_compile 解析,然后把結果存儲在其分配的ip_options結構中。

? ? ip_options_echo:指定入口IP封包及其IP選項后,此函數(shù)就可以建立用于回復傳送者的IP選項。

ip_options_compile函數(shù):

原型:int ip_options_compile(struct ip_options *opt ,struct sk_buff *skb)

當skb不為NULL時(本例中opt為NULL),表示正在處理入口封包。當skb為NULL時(本例中,opt不為NULL),表示正在處理本地傳輸?shù)姆獍?/p>

在傳輸一個本地封包時,opt不為NULL,opt->data包含一個指向IP報頭的指針。處理入口封包時,opt為NULL,報頭包含在skb中,ip_options結構存儲在skb->cb。ip_options_compile函數(shù)會根據(jù)IP報頭位于何處而對本地變量做初始化。

總結

以上是生活随笔為你收集整理的深入理解Linux网络技术内幕学习笔记第十九章:因特网协议第四版(IPv4):Linux的原理和功能的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。