網路層的七七八八

佟暉發表於2023-10-26

網路層主要實現點對點通訊,路由器透過路由表和IP地址進行路由控制,再委託資料鏈路層將資料包傳送到最終目標地址,在連線到網路的不同資料鏈路中進行通訊,是實現全球網路互聯的關鍵部分。

IP

相關概念

IP地址的格式我們都知道,透過CIDR將32位的IP地址分為網路號和主機號。網路定址所需要的網路號可以透過IP地址和子網掩碼做與運算求出。

廣播

廣播地址分為本地廣播和直接廣播,直接廣播出於安全考慮大多數情況會在路由器上設定不轉發。
本地廣播像之前文章中DHCP用到的,主機沒有IP地址,也不知道網段的地址,目的IP設為255.255.255.255會被廣播到相同資料鏈路上的所有主機(乙太網透過將MAC地址設定為ff:ff:ff:ff:ff:ff在同資料鏈路上廣播出去),但是不會被路由器轉發到其他的網段去。
直接廣播:直接廣播地址是主機位全1,例如192.168.1.255/24,將一個資料包傳送到該地址,那麼192.168.1.1~192.168.1.254的主機都會接收到。

多播

多播主要用於給其他網段指定的多臺主機,通常透過路由器複製資料包實現,多播使用D類地址,即首四位固定為1110的地址,地址段244.0.0.0 ~ 239.255.255.255。有一些地址被明確規定了作用,例如:224.0.0.1組會將包傳送到路由器之外的所有主機,224.0.0.2的組將包傳送到所有路由器。
IPV4的多播依賴於IGMP,IGMP有兩大作用:

  1. 通知路由器想要接受多播訊息,路由器收到通知後會將該訊息轉發到其他路由器。
  2. 通知交換機想要接收的多播地址,支援IGMP的交換機就會過濾多播地址,因為多播是基於IP的,而我們知道普通的二層交換機只能處理資料鏈路層的包,所以支援IGMP的交換機需要同時支援解析IP的包。

localhost

環回地址(loopback)就是讓主機自己給自己發資料包,包不會流向網路,127.0.0.1 ~ 127.255.255.255段的地址都是環回地址,IP協議棧解析到該類地址時不會丟到資料鏈路層去封裝,而是直接透過IP棧處理。cat /etc/hosts檢視域名配置:

透過netstat檢視localhost的使用情況:

Tips:透過ping 127.0.0.1判斷IP協議棧本身是否正常

IP首部

IP報文格式如下圖:

各欄位含義

  • Version:IP首部版本號,常用IPv4的值為4,IPv6的值為6
  • IHL(Internet Header Length):IP首部的大小,單位為4bytes,沒有可選項(黃色部分Options)的IP包長度為5,5 * 4 bytes = 20 bytes
  • TOS(Type Of Service):由應用來設定吞吐、延遲、優先順序等特性。現在拆分出了DSCP(Differential Services Codepoin)和ECN(Explicit Congestion Notification)。
    • DSCP:主要提供包的優先順序控制,透過配置DiffServ域,在域中的路由器會根據DSCP值進行優先順序處理,網路擁塞時甚至可以丟棄低優先順序的包
    • ECN:報告網路擁堵情況,如果路由器檢測到網路擁堵,會設定ECN為11,即ECT值為1,CE值為1,從而通知傳送端TCP網路擁堵,傳送端會調節擁塞視窗減慢傳送速率
      • ECT(ECN-Capable Transport):告訴上層TCP是否處理ECN,1為處理
      • CE(Congenstion Experienced):出現網路擁堵情況,1為擁堵
  • Total Length:IP首部和Data總長度,最大為65535 Bytes(16位,2的16次方),受資料鏈路層MTU長度限制,實際傳輸中無法一次傳輸這麼長的資料,需要透過IP分片來處理,傳送端IP層分片接收端IP層重組,因此對於上層協議棧來說,包的最大長度是可以達到65535的。
  • ID(Identification):用於分片和重組,IP包的身份標識,IP包分片後該標識會被放置到每個分片上。接收端會根據ID和片偏移(FO)對包進行重組
  • Flags:表示包分片的相關資訊,第一位的值是0無意義,第二位(0:可以分片,1:不可分片),第三位表示是否是最後一個分片(0:是最後一個分片,1:是中間的分片)
  • FO(Fragment Offset):第一個分片的偏移量為0,13bit總共可以表示8192(2的13次方)個偏移量,偏移的單位為8位元組,可以表示的資料量為8 * 8192 = 65536
  • TTL(Time To Live):Ping命令中常見的欄位,表示包可以經過多少個路由器,每經過一個就減少1,TTL為0時包被丟棄
  • Protocol:是IP層上層協議型別的編號,比如ICMP:1、IPv4:4、IPv6:41、UDP:178
  • Header Checksum:校驗IP資料包首部是否被破壞,不校驗資料部分。
  • src.addr(Source Address):傳送端IP地址
  • dst.addr(Destination Address):接收端IP地址
  • Options:進行網路診斷時使用,長度可變,包含安全級別、源路徑、路徑記錄、時間戳
  • Padding:Options可能會導致首部長度不是32 bit的整數倍(根據IHL欄位IP首部大小的單位為4Bytes,32 bits),Padding會填0將IP首部長度調整為32 bit的整數倍。
  • Data:包含上層協議的首部與資料

路由控制

路由的含義是透過路由器的不斷轉發,讓資料包最終達到目的地址的。路由器根據路由控制表與目的IP地址進行轉發,確保資料包達到目標主機。大概流程是這樣的:
包從主機網路卡發出到第一個路由器 ->
第一個路由器拿到目的地址後查路由表,確定下一跳要走的路由器 ->
確定好下一個路由器的IP後,需要委託資料鏈路層將包發出去,透過ARP拿到下一個路由器的MAC地址後,生成新的MAC首部,透過資料鏈路層傳送(具體操作看這裡)->
包被不斷地轉發由最終目的主機接收。

下面看如何控制路由策略和路由表,路由策略的配置方式分為靜態路由和動態路由:

靜態路由

路由表控制

透過ip route命令可以進行路由表的管理

# 傳送到10.10.10.11的包需要經過10.211.55.3這一跳,從enp0s5這個網路卡出去
ip route add 10.10.10.11 via 10.211.55.3 dev enp0s5
# 透過ip route檢視
root@yielde:~# ip route
10.10.10.11 via 10.211.55.3 dev enp0s5

另外ip route還可以看到幾條預設路由,例如

10.211.55.0/24 dev enp0s5 proto kernel scope link src 10.211.55.3 metric 100

# proto kernel: 該路由由核心新增
# scope link:表明可以透過enp0s5與網段10.211.55.0/24的任何地址通訊
# dev enp0s5:使用網路卡enp0s5
# src 10.211.55.3:使用該網路卡路由時使用的源地址是 10.211.55.3
# metric 100:路由的優先順序,靜態路由的metric為100,值越小優先順序越高

路由規則

可以配置多個路由表,根據IP地址、裝置、和IP首部中的TOC來控制不同型別的包透過不同的路由表走

# 透過10.211.51.0/24網段來的包走路由表20,10.211.52.0/24來的走30
ip rule add from 10.211.51.0/24 table 20
ip rule add from 10.211.52.0/24 table 30

綜合使用ip route和ip rule讓特定的主機走特定的路由表,比如我們讓192.168.1.101主機的包透過自定義的路由表走IP為60.190.27.189網口出去,可以新增這樣的規則:

# 新增路由表
echo 200 t1 >> /etc/iproute2/rt_tables

# 新增路由表規則,包透過網口eth3閘道器為60.190.27.189出去
ip route add default via 60.190.27.189 dev eth3 table t1

# 新增規則,讓該主機的包走路由表t1
ip rule add from 192.168.1.101 table t1

ip route flush cache

iptables

ip routeip rule都是決定包該怎麼走,iptables則可以對過來的包做控制,比如丟掉某個網段過來的包,或者拒絕ICMP協議包等操作,這篇博文有大量示例:
https://www.cnblogs.com/ggjucheng/archive/2012/08/19/2646476.html

動態路由

在實際的網路使用中靜態路由和動態路由時一起使用的。靜態路由只適合對少量節點進行特定的通訊配置,如果涉及大量的資料鏈路和路由節點,節點故障會對網路通訊造成嚴重的影響,而且需要大量的人工干預,這個時候就需要路由器自己來做些事情,保證網路的正常通訊,例如自動維護路由表、路由規則,繞過故障節點等。
這裡面有兩個問題第一個是如何又快又準確的將包送到?第二個是全世界有那麼多網路節點,如何管理?先看看我們是怎樣接入網際網路的。

網際網路的連線

忽略資料鏈路層或應用層的裝置,簡單想想以下各點就是路由器

  1. 運營商下設多個接入點供使用者接入(POP),這些POP會接入到運營商的NOC(Network Operation Center),NOC其實可以看成是效能更強的POP,NOC和POP,POP和POP之間互聯,實現運營商直接或運營商內各接入點的通訊。把運營商提供的這種區域稱為AS(Autonomous System)
  2. 因為運營商的種類和數量過於龐大,將他們都一一互連是不現實的,於是又往上加了一層IX(Internet eXchange),不同的運營商接入IX,透過IX進行通訊。
  3. 運營商內部使用IGP進行通訊,世界各網路運營商使用EGP進行通訊。

IGP(Interior Gateway Protocol)

內部閘道器協議主要用於資料中心內部的路由決策。一種是OSPF協議,基於鏈路狀態路由協議實現,鏈路狀態路由協議是說每一臺路由器本地都儲存著一樣的網路拓撲結構,如果鄰居下線或者出現新的鄰居導致拓撲結構發生變化,則會將變化的部分廣播給其他的路由器,其他路由器更新本地拓撲結構。當發生通訊時,根據該拓撲圖使用Dijkstra演算法找到最短路徑將包轉傳送達,因為它只廣播拓撲變化的部分,並且各節點之間可以及時同步,所以是更適用於ISP內部的路由方式。

EGP(Exterior Gateway Protocol)

外部閘道器協議主要用於AS之間的通訊。一種是邊界閘道器協議(BGP),BGP透過TCP實現,現在AS之間通訊使用eBGP,保證可靠性的同時可以對包的傳輸和控制做更多的操作。
BGP會生成一個AS Path List,會記錄到達目標地址的多條路徑、不同路徑的距離、以及經過的AS的編號,通常會選擇途徑AS最少的路徑進行傳輸。

分片與重組

我們知道資料鏈路層有MTU的限制,例如乙太網預設MTU為1500 Bytes,IP層受Total Length的長度限制,最大為65535,但是IP包傳輸是需要委託資料鏈路層來傳送的,超過了資料鏈路層MTU就需要對IP包進行分片,除最後一個分片,其餘分片單位為8Bytes的倍數。分片可以由路由器來完成,而重組只能由最終的主機來做(因為每個分片的傳輸路徑可能不同,而且網路層的傳輸是不可靠的,中間的分片可能會丟失)。

分片

首部中記錄的ID、Flags、Fo欄位就是作用於分片重組的,ID唯一標識了每個包,Flags可以拿到是否是最後一個分片,Fo是每個分片相對於第一個分片的偏移量,目的主機可以透過這三個欄位來進行重組。分片步驟:

  1. 檢查Flags的第二位,0為可分片,1為不可分片,如果設定了1並且包的長度超過了鏈路層的MTU,則路由器會丟棄該包,並且傳送ICMP目標不可達差錯報文(Type:3,錯誤程式碼為4,下文ICMP有介紹)
  2. 除了最後一個分片,其餘分片長度為8Bytes的整數倍(Fo欄位的單位為8Bytes),基於MTU計算分片長度
  3. 設定Flags的第三位,最後一片值為0,其餘為1
  4. 設定Fo,相對於包開頭的偏移量
  5. 生成ID並將ID複製到每個分片(透過首部Options設定,也可以只設定ID到第一個分片)
  6. 重新計算並設定IP首部的Total Length和Header Checksum

委託資料鏈路層進行傳送,傳送途中如果分片的長度大於某一裝置的MTU,則會再進行分片。

重組

目的主機需要準備一個緩衝區用於存放分片,需要準備一個計時器,找到ID相同的分片,透過Fo和Flags將分片組裝起來,如果重組過程中發生超時,會傳送一個傳輸超時的ICMP差錯報文(Type:11,錯誤碼為1)並清空緩衝區。

路徑MTU發現

路由器分片會增加路由器的負載,另外分片處理中,如果一個分片丟失,則IP包作廢,之前的分片和傳輸操作都是無用功。路徑MTU讓傳送端主機來做分片工作,路由器只負責溝通和傳輸分片。具體做法:

  1. 在傳送端主機將Flags第二位設定為1禁止分片,如果路由器發現該包需要分片才能傳輸,會丟棄並透過ICMP差錯報文告訴主機MTU
  2. 主機根據拿到的MTU對包進行分片處理,主機會快取該MTU最少10分鐘的時間,後續包傳送會根據該MTU進行分片,如果超過快取時間,後續傳送會再次進行路徑MTU發現操作

UDP的包會在下一次傳送時交給IP協議棧來處理分片,而TCP有重發機制,所以會直接在TCP協議棧進行分片,然後交給IP協議棧進行打包和傳送(不需要IP層來分片)。

網路層相關技術

ICMP(Internet Control Message Protocol)

ICMP是封裝在IP包裡面的,ICMP最多的應用場景就是用來確認網路是否正常和進行診斷的,它可以返回包沒有被送達的原因。ICMP有很多型別,常用的如下:
0:主動應答
3:目標不可達
5:路由重定向
8:主動請求
11:傳輸超時

查詢報文

ping

查詢報文用於診斷網路,我們用的ping就是基於ICMP工作的,只不過多出了Identifier和Sequence Number用於標識ping不同的地址和ping包的傳送順序,ping命令抓包如下:

我們看到有BE和LE的分類,是因為ping報文在windows系統和linux系統儲存方式不同,windows是小端序LE(little-endian),Linux是大端序(big-endian),wireshark顯示了兩種序列。

透過Type:8可以看到這是一個主動請求的包,Timestamp儲存傳送時間戳用來計算往返時間。
抓包一個應答包:

差錯報文

差錯報文是當網路發生故障時通知錯誤原因,
目標不可達(Type:3),當IP資料包無法到達目的地址時,會給傳送端返回一個目標不可達的訊息,這個訊息中會包含錯誤碼,例如:
0:網路不可達
1:主機不可達
2:協議不可達
3:埠不可達
4:需要分片但是設定了不可分片標誌位
7:未知的目標主機
路由重定向(Type:5),路由器發現傳送包使用的路徑不是最優路徑時,會傳送該訊息告知傳送端有一條更快的路徑,傳送端將該路徑追加到路由表中,下次傳送走這條路徑。
傳輸超時(Type:11),上面IP首部的TTL欄位,如果包傳輸的TTL被減為0而丟棄,路由器會傳送一個傳輸超時的訊息給傳送端,錯誤碼為0(錯誤碼為1表示包在重組時超時),告知傳送端該包已經被丟棄。

traceroute

除了遇到真正的錯誤,traceroute會使用icmp的規則設定不合理的值來達到不同的目的。比如跟蹤一個包,拿到它的中間路由。

  1. 設定TTL為1,向指定地址發UDP資料包,我們知道每經過一跳路由,TTL減1,減到0這個包就被丟掉了。
  2. 路由器給主機傳送時間超時的ICMP差錯報文
  3. 繼續傳送設定TTL為2,去拿第二跳的地址
  4. traceroute設定的UDP埠號大於30000,如果主機收到了會返回一個埠不可達的報文,這時traceroute就知道包已經送到主機了

traceroute公網地址的時候有時候看不到中間路由,因為路由器做了設定不會發回ICMP差錯報文

DNS(Domain Name System)

給IP地址取個名字,比如我們在配置分散式系統時,沒有DNS伺服器通常會在hosts檔案中寫一條主機名和IP地址的對映。DNS系統就是維護這種對映的一個系統,從根域名伺服器開始分層大致分為:
根DNS:返回頂級域DNS的IP地址
頂級域DNS:返回權威DNS的IP地址
權威DNS:返回主機的IP地址
主機上會配置本地DNS伺服器(一般就是配置的DHCP),主機透過DNS解析器向域名伺服器發起查詢。

DNS解析

  1. 客戶端先檢視hosts檔案,如果有直接向該IP傳送訊息,如果沒有需要透過DNS解析器(Resolver)去請求本地DNS伺服器,以請求www.baidu.com為例
  2. 本地DNS收到請求後,如果快取中有該對映返回給客戶端,如果沒有就向根DNS伺服器請求
  3. 根DNS會返回管理該域名的下一層DNS的IP地址給本地DNS,就是頂級DNS的IP地址(管理.com)
  4. 本地DNS繼續請求頂級DNS(.com、.net、.org等域名),它們儲存著baidu.com等權威DNS的地址,將權威DNS的IP地址返回給本地DNS(管理baidu.com)
  5. 本地DNS請求權威DNS(baidu.com),權威DNS將客戶端要的www.baidu.com的IP地址返回到本地DNS
  6. 本地DNS將最終的IP地址返回給客戶端

NAT(Network Address Translator)

IPv4的地址越來越稀少,我們知道同一資料鏈路的IP地址如果相同的話會引起衝突。進入網際網路通訊需要全域性IP地址,全球有那麼多使用者,IPv4根本不夠給每個使用者分配一個全域性IPv4地址,就像我們想在外面訪問家裡的電腦一樣,以前可以給運營商打電話申請一個全域性IP地址將電腦暴露在公網上直接訪問,現在給運營商打電話只能花錢買,所以需要一些內網穿透的技術來做。
NAT就是在訪問網際網路時將本地的私有地址轉換為全域性IP地址的的技術,也是生成一個私有IP與全域性IP的對映表,如果私網中有多個主機要訪問網際網路,單單IP對映沒法區分到底要路由到哪臺主機,於是又加入了埠號,透過埠號和IP來做對映,這種技術叫NAPT(Network Address Port Translation)。

比方我們的內網有兩臺主機11.11.11.1111.11.11.22,透過相同的TCP埠888來訪問目標伺服器200.200.200.200:80,包到達路由器後需要將這兩個地址轉換為路由器的全域性IP地址33.33.33.33,如果沒有埠對映,那麼收包的時候路由器就無法確認該把這個包交給哪臺主機,透過NAPT技術,路由器將11.11.11.11:888對映為33.33.33.33:222,將11.11.11.22:888對映為33.33.33.33.:111,透過這兩個地址與伺服器通訊,收包後直接根據對映表發給對應主機即可。

在TCP中,首次握手的SYN包發出就會在表中新增一條對映記錄,關閉連線時收到FIN包的確認應答後從表中刪除這條對映

DHCP(Dynamic Host Configuration Protocol)

自動分配IP地址,在我DHCP和PXE是怎麼工作的文章中有記錄。

ARP(Address Resolution Protocol)

透過IP地址找MAC地址,上一篇文章的資料鏈路層的七七八八中有記錄,但是ARP只能用於IPv4,IPv6需要使用ICMPv6來完成。

學習自:
《趣談網路協議》劉超
《圖解TCP/IP》
《圖解HTTP》
《網路是怎樣連線的》

相關文章