CIDR 是什麼意思:無類域間路由與 /n 斜線寫法
CIDR(Classless Inter-Domain Routing,無類域間路由)是 1993 年起取代 A、B、C 類位址的分配與路由方式:網路部分不再固定為 8、16、24 位,而是寫在斜線後面。/22 就表示前 22 位固定、後 10 位可變,即一個含 1,024 個位址的塊。
CIDR 是什麼意思:它是 Classless Inter-Domain Routing 的縮寫,中文叫無類域間路由,是今天網際網路分配 IP 位址和做路由的基本規則。它做了兩件事:一是取消 A、B、C 類對網路位長度的限制,網路部分可以是 0 到 32 之間的任意位數,寫在位址後面的斜線裡,例如 172.18.20.0/22;二是允許把連續的位址塊彙總成一條路由。日常說的“CIDR 寫法”“CIDR 位址塊”,指的就是這種“起始位址/字首長度”的表示法,它同時說清了一個塊從哪裡開始、有多大。
有類位址為什麼被 CIDR 取代
CIDR 之前,IPv4 位址按第一個位元組分類,網路位只有三種長度:
| 類別 | 首位元組範圍 | 網路位 | 這一類共有多少個網路 | 每個網路可用主機數 |
|---|---|---|---|---|
| A 類 | 1–126 | 8 位 | 126 | 16,777,214 |
| B 類 | 128–191 | 16 位 | 16,384 | 65,534 |
| C 類 | 192–223 | 24 位 | 2,097,152 | 254 |
問題出在檔位太粗。一個需要 2,000 台主機的單位,C 類的 254 個不夠用,只能申請 B 類,結果 65,534 個位址只用了約 3%。B 類總共只有 16,384 個,按這種方式發放,很快就不夠分了;退而求其次,給這個單位發 8 個 C 類,位址省下了,可每個 C 類都要在網際網路路由器上單獨佔一條路由,路由表隨之膨脹。
1993 年釋出的 RFC 1519 把當時的困境概括為三點:B 類位址即將耗盡、網際網路路由器上的路由表增長過快,超出現有軟體和人力能夠有效管理的程度、32 位位址空間終將用完。CIDR 針對前兩點給出了辦法:
- 字首長度任意:需要 2,000 個位址就給一個 /21(2,048 個位址,2,046 個可用),不必在 254 和 65,534 之間二選一。
- 按層級分配、對外匯總:位址從序號產生器構分給運營商,再由運營商分給客戶。運營商手裡成百上千個客戶的小塊,對外只宣告一個彙總後的大字首。
RFC 1519 後來被 2006 年的 RFC 4632 取代,規則延續至今。原來的“子網劃分”只能在一個有類網路內部往小裡切,CIDR 則讓塊可以比 /8、/16、/24 更大或更小,位址和路由都只看字首長度,不再看第一個位元組。
/n 怎麼讀:前 n 位固定,剩下的可變
CIDR 寫法 位址/n 的意思是:32 位位址裡,前 n 位是網路字首,塊內所有位址的這 n 位都相同;後面 32 − n 位可以任意變化。由此直接得到三個數:
- 塊大小:2 的(32 − n)次方。/22 是 2^10 = 1,024 個位址。
- 掩碼:n 個 1 後面補 0。/22 是 255.255.252.0。
- 起點:起始位址必須讓後 32 − n 位全為 0,也就是塊大小的整數倍。
n 正好是 8、16、24 時最直觀,斜線前面的位址按點對齊,/24 就是前三節固定、第四節從 0 到 255。n 不是 8 的倍數時,有一個位元組會被字首切成兩半,這個位元組決定了塊的邊界。字首在 /17 到 /24 之間,被切開的是第三節:
| 字首 | 第三節的步長 | 一個 /16 裡能分出幾個 | 第三節的合法起點 | 塊大小 |
|---|---|---|---|---|
| /17 | 128 | 2 | 0、128 | 32,768 |
| /18 | 64 | 4 | 0、64、128、192 | 16,384 |
| /19 | 32 | 8 | 0、32、64……224 | 8,192 |
| /20 | 16 | 16 | 0、16、32……240 | 4,096 |
| /21 | 8 | 32 | 0、8、16……248 | 2,048 |
| /22 | 4 | 64 | 0、4、8……252 | 1,024 |
| /23 | 2 | 128 | 所有偶數 | 512 |
| /24 | 1 | 256 | 0–255 任意 | 256 |
/25 到 /32 的規律完全相同,只是被切開的換成第四節:/25 步長 128,/26 步長 64,一直到 /30 步長 4。任何字首都能現場推出步長:先求 n 除以 8 的餘數 r,步長就是 2 的(8 − r)次方。例如 /22 的 r = 6,步長 2^2 = 4;/27 的 r = 3,第四節步長 2^5 = 32。r 為 0 說明正好按位元組對齊,不需要這個公式。
非整位元組的例子:/22 和 /23 怎麼看
例一:172.18.20.0/22 包含哪些位址。 22 − 16 = 6,第三節的前 6 位屬於字首,後 2 位和第四節的 8 位一起可變:
172.18.20.0/22
第三節 20 = 000101|00 前 6 位固定(16 + 6 = 22)
^^ 這 2 位加上第四節 8 位,共 10 位可變
第三節可取 000101 00、01、10、11,即 20、21、22、23
範圍:172.18.20.0 ~ 172.18.23.255,共 2^10 = 1,024 個位址
所以 /22 等於 4 個連續的 /24,第三節從 20 到 23。作為一個子網使用時,網路位址是 172.18.20.0,廣播位址是 172.18.23.255,中間 1,022 個都能分配。
例二:172.18.27.130/22 屬於哪個塊。 看第三節,/22 的步長是 4,27 除以 4 得 6 餘 3,塊從 6 × 4 = 24 開始,所以它屬於 172.18.24.0/22,範圍 172.18.24.0 到 172.18.27.255。
例三:172.18.26.0/23。 步長 2,第三節 26 是偶數,合法起點。範圍是 172.18.26.0 到 172.18.27.255,共 512 個位址。在這個塊裡,172.18.26.255 和 172.18.27.0 都是普通的主機位址,可以分給伺服器;真正的廣播位址只有末尾的 172.18.27.255。
這幾個例子對應幾種常見的誤讀:
| 寫法 | 常見誤讀 | 實際含義 |
|---|---|---|
| 172.18.20.0/22 | 只有 172.18.20.x 這一段 | 20、21、22、23 四個 /24 |
| 172.18.22.0/22 | 一個從第三節 22 開始的塊 | 22 不是 4 的倍數,不是合法起點;這個位址落在 172.18.20.0/22 裡 |
| 172.18.26.0/23 裡的 172.18.26.255 | 末位是 255,是廣播位址 | 普通可用位址,廣播位址是 172.18.27.255 |
| 任意兩個相鄰的 /24 | 都能合成一個 /23 | 只有第三節以偶數開頭的一對才行,26 和 27 可以,27 和 28 不行 |
第三行在實際工作中最容易出事:一些寫死了“末位 0 或 255 不能用”的表單校驗和老指令碼,會把 /23、/22 中間的這類位址誤判為非法。分配位址前,按塊的邊界判斷,不要按末位數字判斷。
位址塊和字首:一個塊只有一種合法寫法
字首和位址塊是一回事的兩種說法:字首是塊內所有位址共有的那 n 位,位址塊是共有這 n 位的全部位址。所以一個合法的塊只有一種寫法,起點和長度都是唯一的。斜線前的位址如果不是塊的起點,也就是主機位不全為 0,那它描述的就不是一個塊,很多工具會直接拒絕:
# 主機位不為 0,作為路由目標是非法寫法
$ ip route add 172.18.22.0/22 via 10.0.0.1
Error: Invalid prefix for given prefix length.
# Python 同樣拒絕,報錯的最後一行是:
$ python3 -c "import ipaddress; ipaddress.ip_network('172.18.22.0/22')"
ValueError: 172.18.22.0/22 has host bits set
# 加 strict=False,讓它歸到所在的塊
$ python3 -c "import ipaddress; print(ipaddress.ip_network('172.18.22.0/22', strict=False))"
172.18.20.0/22
但同樣的斜線寫法配在網路卡上就是合法的:ip addr add 172.18.22.15/22 dev eth0 的意思是“本機位址 172.18.22.15,所在塊的字首長度是 22”,系統會自動算出所在的網段 172.18.20.0/22 並加上直連路由。區分方法很簡單:路由表、防火牆規則、分配單、IPAM 登記裡寫的是“塊”,斜線前必須是起點;網路卡配置裡寫的是“位址加字首長度”,斜線前是主機自己的位址。
反過來,一段不按邊界對齊的連續位址,不能用一個 CIDR 表示。例如 172.18.21.0 到 172.18.24.255,雖然恰好是 4 個 /24,但起點 21 不是 4 的倍數,湊不成一個 /22,最少要拆成三塊:
$ python3 -c "import ipaddress as i; print(*i.summarize_address_range(i.ip_address('172.18.21.0'), i.ip_address('172.18.24.255')))"
172.18.21.0/24 172.18.22.0/23 172.18.24.0/24
拿到“從某某到某某”這種起止範圍的分配單時,先用這條命令轉換成 CIDR,再錄入防火牆或路由配置。
路由器怎麼用 CIDR:最長字首匹配
有了任意長度的字首,一個目的位址可能同時落在好幾條路由裡。路由器的規則是最長字首匹配:能匹配上的路由中,字首最長、也就是塊最小、描述最具體的那條勝出。假設一台路由器上有這四條路由:
| 目的字首 | 下一跳 | 能匹配 172.18.22.9 嗎 |
|---|---|---|
| 0.0.0.0/0 | 10.0.0.1(出口) | 能,/0 匹配一切 |
| 172.18.0.0/16 | 10.0.1.1 | 能 |
| 172.18.20.0/22 | 10.0.2.1 | 能,22 在 20~23 之間 |
| 172.18.22.0/24 | 10.0.3.1 | 能 |
發往 172.18.22.9 的包四條全中,選 /24,交給 10.0.3.1;發往 172.18.21.9 的包匹配不上 /24,選 /22,交給 10.0.2.1;發往 172.18.40.1 的只匹配 /16 和 /0,選 /16;8.8.8.8 只剩預設路由。想知道裝置對某個位址實際選了哪條,直接問它:
# Linux:輸出實際選中的下一跳和出介面
ip route get 172.18.22.9
# 172.18.22.9 via 10.0.3.1 dev eth0 src 10.0.0.10
華為和 H3C 用 display ip routing-table 172.18.22.9,思科用 show ip route 172.18.22.9,都會顯示最終匹配的那條路由。
這條規則是 CIDR 彙總能成立的前提:運營商對外宣告一個大字首,內部再用更長的字首把流量分到各個客戶,互不衝突。它也解釋了機房裡的一些常見現象,例如流量清洗服務在攻擊期間對外宣告比原路由更長的字首,就能把這部分位址的流量引到清洗中心;有人在內網配錯了一條更具體的靜態路由,大段位址裡只有那一小塊不通。
在 IPAM 裡怎麼落地
機房的位址台帳裡,最常見的錯誤不是不會算,而是寫法不統一:有的段寫起止位址,有的寫 CIDR,有的把 172.18.22.0/22 這種非起點寫法直接錄進去,幾年下來誰也說不清哪些塊重疊了。
Toplink DCIM 的 IP 位址管理以 CIDR 為錄入格式:填入一個段,系統自動算出開始位址、結束位址和子網掩碼,閘道器可以選段內第一個或最後一個位址;/22、/21 這類大段可以自動拆成多個 /24 分別管理,每個段再記錄所在機房、VLAN 和閘道器裝置。IPv6 段同樣按 CIDR 錄入、按掩碼拆分。位址按塊登記之後,哪個塊分給了誰、還剩多少,在用量比例圖上一目瞭然。
常見問題
CIDR 和 VLSM 有什麼區別?
VLSM(變長子網掩碼)指在一個網路內部用不同長度的掩碼劃分子網;CIDR 指分配和路由時不再受 A、B、C 類限制,並允許把多個塊彙總成一條路由。前者是內部劃分的方法,後者是整個網際網路的位址與路由規則,兩者經常一起出現。
為什麼說 CIDR 讓路由表變小了?
連續且對齊的位址塊可以彙總成一條字首更短的路由,例如 4 個相鄰的 /24 合成一條 /22。有類時代,同樣的位址要用 4 條 C 類路由分別宣告。彙總要求塊的數量是 2 的冪、位址連續、起點對齊,不滿足時只能拆成多條。
IPv6 也用 CIDR 寫法嗎?
用,而且 IPv6 從設計之初就沒有分類,只有字首長度,例如 2001:db8:1200::/40。字首長度範圍是 0 到 128,區域網段通常固定用 /64,分配給機房或企業的常見 /48、/56。