1. IP協議概述
IP 協議(Internet Protocol),又稱之為網際協議,屬於網絡層。IP協議以IP地址作為唯一識別碼,負責將數據從源主機發送到目標主機。
IP 協議是一種無連接的不可靠數據報交付協議,協議本身不提供任何的錯誤檢查與恢復機制。
1.1 IP地址
在互聯網中,每一個主機都有一個唯一的IP地址作為身份識別標志。
(1)分類編址
IP地址可分為五類:A類、B類、C類、D類、E類,其組成如下圖。
各類IP地址的特點,如下圖。
(2)特殊IP地址
特殊IP地址用於特殊用途,不能分配給任何一個網絡的主機使用。
受限廣播地址 | 網絡號、主機號全為1的地址(255.255.255.255),表示整個互聯網內的主機。但是由於路由器禁止轉發IP地址為255.255.255.255的數據包,這樣的數據包只會在局域網內廣播。 |
直接廣播地址 | 主機號全為1的地址,表示局域網內的所有主機, |
多播地址 | D類地址屬於多播地址,一個發送者,多個接收者。D類地址只能作為目標IP地址,不能作為源IP地址。 |
環回地址 | A類地址中,127網段的所有地址都是環回地址,用來測試網絡協議是否正常工作。譬如,ping 127.1.1.1可以測試本地TCP/IP協議是否正常工作。 |
本網絡本主機 | IP地址全為0的地址(0.0.0.0),表示本網絡本主機。該地址只能作為源地址,用於本機IP地址不明確的情況。 |
1.2 局域網、廣域網、互聯網
局域網(Local Area Network,縮寫為 LAN),又稱內網,覆蓋局部區域的計算機網絡。
廣域網((Wide Area Network,縮寫為 WAN),又稱外網、公網,連接不同區域的計算機網絡進行通信。
互聯網,由無數個局域網,通過廣域網線路匯聚互聯形成。
局域網、廣域網、互聯網三者間關系如下圖所示。
- 無線路由器為手機、電腦分配局域網IP(LAN-IP)。路由器的IP地址,由運營商分配(運營商的局部IP地址),該地址將被轉換為廣域網IP地址(WAN-IP)。廣域網IP地址(WAN-IP)也需要轉換為互聯網公共IP地址(Global-IP),才能進入互聯網。
- 網絡通信的IP地址轉換過程:LAN-IP <—> WAN-IP <—> Global-IP。
1.3 網絡地址轉換
示例:
- 具有NAT功能的路由器擁有兩個IP地址,一個內部地址,用於進行局域網內部的通信;一個外部地址,用於與廣域網進行通信,由運營商分配。
- 具有 NAT 功能的路由器會在其內部維護一個 NAT 轉換表。當路由器收到局域網的IP數據報時,路由器會為該數據報分配一個它內部的 NAT 端口(譬如:port 6666),局域網主機IP地址(192.168.0.181:5555)與廣域網IP地址(223.166.166.66:6666)形成一個映射。從而,局域網主機能夠實現與廣域網進行通信。
- NAT轉換中,路由器會每個連接的局域網主機分配唯一的NAT端口號,並回收失效端口號。
2. IP報文
2.1 IP報文格式
- 版本:IP協議的版本號。IPv4的版本號為4,IPv6的版本號為6。
- 首部長度:記錄IP首部所占的空間,單位“字”。占4bit,因此IP首部的最大長度為15*4=60Byte。
- 服務類型(TOS):用於區分不同的IP數據包。譬如,區分一些特別要求低時延、高吞吐量或可靠性的數據包;從而便於路由器為此類IP數據包提供更合理的路徑。
- 數據報長度:IP報文的總長度,IP首部+數據區域的長度,單位“字節”。該長度一般不超過以太網數據幀的最大長度(MTU_MAX=1500Byte),若超過,則需進行分片發送。若該長度小於MTU_MIN(46Byte),則需填充至MTU_MIN后再發送。
- 標識:用於判斷各個IP數據報分片是否屬於同一個數據報。每發送一個IP數據報,該字段的值+1;屬於同一個IP數據報的分片,該字段的值相等。
- 標志:占3bit。BIT(0)保留未用;BIT(1)為1表示該數據報允許進行分片處理,為0表示禁止分片處理(此時,若數據報長度超過MTU_MAX,則丟棄該數據報);BIT(2)為0表示該分片是整個數據報的最后一個分片,反之則不是。
- 分片偏移量:表示當前分片所攜帶的數據在整個IP數據報中的偏移量,以8Byte為單位。
- 生存時間(TTL):每當IP數據報被一個路由器處理后,該字段的值減1;當減為0時,丟棄該數據報。從而確保IP數據報不會永遠在網絡中循環(譬如由於長時間的路由選擇環路)。
- 上層協議:表示IP數據報的數據部分應該交由哪個傳輸協議(TCP、UDP、)處理。
- 首部校驗和:IP數據報首部的校驗和,用於幫助路由器檢測收到的IP數據報的首部是否正確。
- 源IP地址:源主機的IP地址。
- 目標IP地址:目標主機的IP地址。
- 選擇:該字段不是必須組成部分。LWIP協議棧只識別選項字段,但是不會處理它的內容。
- 數據區域:IP數據報所攜帶額數據。
P.S.:IPv4和IPv6的報文格式不同,此處記錄的為IPv4報文格式。
2.2 IP報文數據結構定義
LWIP定義了ip_hdr結構體來描述IP報文的首部,同時定義了獲取IP報文首部信息的宏定義、設置IP報文首部信息的宏定義。源碼位置:lwip_2_1_2/src/core/ipv4/ip4_frag.c
定義ip_hdr結構體時要禁止編譯器進行對齊操作,因為該結構體的很多字段都是按位進行操作的。

1 PACK_STRUCT_BEGIN 2 struct ip_hdr 3 { 4 /* 版本 / 首部長度 */ 5 PACK_STRUCT_FLD_8(u8_t _v_hl); 6 /* 服務類型 */ 7 PACK_STRUCT_FLD_8(u8_t _tos); 8 /* 數據報總長度 */ 9 PACK_STRUCT_FIELD(u16_t _len); 10 /* 標識字段 */ 11 PACK_STRUCT_FIELD(u16_t _id); 12 /* 標志與偏移 */ 13 PACK_STRUCT_FIELD(u16_t _offset); 14 #define IP_RF 0x8000U /* 保留的標志位 */ 15 #define IP_DF 0x4000U /* 不分片標志位 */ 16 #define IP_MF 0x2000U /* 更多分片標志 */ 17 #define IP_OFFMASK 0x1fffU /* 用於分段的掩碼 */ 18 /* 生存時間 */ 19 PACK_STRUCT_FLD_8(u8_t _ttl); 20 /* 上層協議*/ 21 PACK_STRUCT_FLD_8(u8_t _proto); 22 /* 校驗和 */ 23 PACK_STRUCT_FIELD(u16_t _chksum); 24 /* 源 IP 地址與目標 IP 地址 */ 25 PACK_STRUCT_FLD_S(ip4_addr_p_t src); 26 PACK_STRUCT_FLD_S(ip4_addr_p_t dest); 27 } PACK_STRUCT_STRUCT; 28 PACK_STRUCT_END 29 30 31 /* 獲取 IP 數據報首部各個字段信息的宏 */ 32 33 //獲取協議版本 34 #define IPH_V(hdr) ((hdr)->_v_hl >> 4) 35 //獲取首部長度(字) 36 #define IPH_HL(hdr) ((hdr)->_v_hl & 0x0f) 37 //獲取獲取首部長度字節 38 #define IPH_HL_BYTES(hdr) ((u8_t)(IPH_HL(hdr) * 4)) 39 //獲取服務類型 40 #define IPH_TOS(hdr) ((hdr)->_tos) 41 //獲取數據報長度 42 #define IPH_LEN(hdr) ((hdr)->_len) 43 //獲取數據報標識 44 #define IPH_ID(hdr) ((hdr)->_id) 45 //獲取分片標志位+偏移量 46 #define IPH_OFFSET(hdr) ((hdr)->_offset) 47 //獲取偏移量大小(字節) 48 #define IPH_OFFSET_BYTES(hdr) \ 49 ((u16_t)((lwip_ntohs(IPH_OFFSET(hdr)) & IP_OFFMASK) * 8U)) 50 //獲取生存時間 51 #define IPH_TTL(hdr) ((hdr)->_ttl) 52 //獲取上層協議 53 #define IPH_PROTO(hdr) ((hdr)->_proto) 54 //獲取校驗和 55 #define IPH_CHKSUM(hdr) ((hdr)->_chksum) 56 57 58 /* 用於填寫 IP 數據報首部的宏*/ 59 60 //設置版本號跟首部長度 61 #define IPH_VHL_SET(hdr, v, hl) \ 62 (hdr)->_v_hl = (u8_t)((((v) << 4) | (hl))) 63 //設置服務類型 64 #define IPH_TOS_SET(hdr, tos) (hdr)->_tos = (tos) 65 //設置數據報總長度 66 #define IPH_LEN_SET(hdr, len) (hdr)->_len = (len) 67 //設置標識 68 #define IPH_ID_SET(hdr, id) (hdr)->_id = (id) 69 //設置分片標志與偏移量 70 #define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off) 71 //設置生存時間 72 #define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl = (u8_t)(ttl) 73 //設置上層協議 74 #define IPH_PROTO_SET(hdr, proto) (hdr)->_proto = (u8_t)(proto) 75 //設置校驗和 76 #define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum)
3. IP數據報分片
(1)分片處理的原因
IP數據報被傳送至鏈路層,鏈路層將IP數據報封裝成鏈路層幀,網卡硬件將鏈路層幀發送至目標主機。一個鏈路層幀能攜帶的最大數據量稱為最大傳送單元(MTU),不同網卡硬件的MTU可能不一樣。當IP數據報的長度超過MTU,則需對其進行分片處理后,再發送。
(2)分片處理的原理
IP數據報的分片處理,是將IP數據報中的數據區域切割為若干個較小的IP數據報,並封裝成單獨的鏈路層幀后進行發送。
- 每個分片的IP首部,除“標志”、“分片偏移量”這兩個字段,其余都一樣。
- “分片偏移量”字段的單位是8Byte,因此分片的數據區域的長度也必須是8的整數倍。
- 最后一個分片的標志位的BIT(2)為0,表示該分片為最后一個分片。
- 所有分片在到達目標主機的IP層后,進行重裝,形成一個完整的IP數據包。
(3)分片處理示例
主機發送一個4000Byte的IP數據報,鏈路層MTU = 1500Byte,需要對原始IP數據報進行分片處理。
原始IP數據報(4000Byte)= IP首部(20Byte)+ 數據區域(3980Byte),對數據區域進行切割,並填寫每個分片IP首部的“標志”、“分片偏移量”字段。
第一個分片:“標志”字段BIT(2) = 1,分片偏移量為0,分片數據報長度(1500Byte)= IP首部(20Byte)+ 數據區域(1480Byte);
第二個分片:“標志”字段BIT(2) = 1,分片偏移量為185(1480/8),分片數據報長度(1500Byte)= IP首部(20Byte)+ 數據區域(1480Byte);
第三個分片:“標志”字段BIT(2) = 0,分片偏移量為370(185+185),分片數據報長度(1040Byte)= IP首部(20Byte)+ 數據區域(1020Byte)。
(4)分片處理的源碼
分片處理的源碼位於“lwip_2_1_2/src/core/ipv4/ip4_frag.c”,調用“ip4_frag()”函數對IP數據報進行分片處理並發送至目標主機。
4. IP數據報的發送與接收
IP數據報的發送與接收,源碼位於:lwip_2_1_2/src/core/ipv4/ip4.c
4.1 IP數據報的發送
- 傳輸層需要發送數據時,將需要發送的數據傳遞到網絡層的IP協議,IP層調用“ip4_output()”函數發送IP數據報。
- “ip4_output()”函數調用“ip4_route_src()”函數根據目標IP地址在網卡列表中選擇一個合適的網卡,匹配條件:目標IP地址與網卡地址在一個子網內,或者目標IP地址等於網卡的網關地址。再調用“ip4_output_if()”函數發送IP數據報。
- “ip4_output_if_src()”函數調整payload指針至IP數據報首部的起始地址,填寫IP首部信息,並對IP數據報進行分類處理。若目標IP地址等於源主機的IP地址,則調用“netif_loop_output()”函數進行環回輸入;若IP數據報長度超過MTU,則調用“ip4_frag()”函數進行分片發送;否則直接調用“netif->output ”接口將IP數據報傳遞給ARP協議進行發送。
4.2 IP數據報的接收
- 鏈路層接收到一個IP數據報,通過“ethernet_input()”函數再傳遞到“ip4_input()”函數進行處理。
- “ip4_input()”函數首先檢查IP數據報首部的協議版本是否為IPv4,然后根據目標IP地址在本地網卡列表中尋找有效的目標網卡。若目標IP地址不等於主機地址,則通過“ip4_addr_isloopback()”函數判斷目標IP地址是否為環回地址。若匹配到目標網卡,對於分片則調用“ip4_reass()”函數進行重裝,然后將數據傳遞至指定的傳輸層協議。若沒有找到目標網卡,對於廣播包則直接刪除該數據報,對於非廣播包則可以通過“ip4_forward()”函數進行轉發。
5. 參考資料
[1] 野火《LwIP應用開發實戰指南》。