子網路計算
IPv4 與 IPv6 的 CIDR 拆解:網段、廣播、可用範圍、遮罩與切分子網。純算術。
原理
子網路計算只有一個動作:用前綴長度造出遮罩,再跟位址做位元運算。/24 的遮罩就是前 24 個位元是 1、後 8 個是 0,把位址跟它做 AND 得到網段位址,跟它的反向遮罩做 OR 得到區塊的最後一個位址。IPv4 與 IPv6 的差別只在位址寬度是 32 還是 128 位元,規則完全一樣。
這裡兩個家族都存成 bigint,不是因為 IPv6 需要,而是因為 IPv4 用 JavaScript 的位元運算會出錯:`&`、`|`、`~` 會先把運算元轉成有號 32 位元整數,所以 0.0.0.0/1 的反向遮罩算出來是負數,之後每一個比較都跟著錯。一種數字型別處理兩個家族,直接消掉這一整類 bug。
可用主機數是各家計算器最常給出不同答案的地方,所以三種情況分開寫。/30 以下扣掉網段位址與廣播位址,是 2^n − 2。/31 沒有廣播位址:RFC 3021 把兩個位址都給點對點鏈路的兩端,可用數是 2。/32 是一台主機,沒有網段的概念。IPv6 根本沒有廣播——它的位置由多播取代——所以整個區塊都可以配置;全零主機部分被保留為子網路路由器 anycast(RFC 4291 §2.6.1),但那是給路由器的保留,不從數量裡扣。
「位址性質」那一欄是照 IANA 特殊用途登錄表做最長前綴比對,跟路由表解析的方式一樣,所以 2001:db8::1 會判成文件用位址而不是 Teredo,即使 2001::/32 也涵蓋它。範圍轉 CIDR 用的是對齊法:每一步取「能從目前位置開始、又不超出結尾」的最大區塊,`cursor & -cursor` 取出最低位的 1,那就是目前位置的對齊度,也就是能合法起始的最大區塊大小。
界線
- IPv4 的前導零一律拒絕。010.0.0.1 在這個剖析器裡是十,在 inet_aton 裡是八,而哪一家函式庫用哪一種解讀就是一長串 SSRF 繞過的來源——拒絕這種寫法是唯一不會默默算錯的選擇。
- 切分子網段與範圍轉 CIDR 都有列表上限。把 /8 切成 /32 是四十億列,那不是使用者要看的東西,超過上限時會說明只列出前面幾筆,而不是把分頁凍住。
- 這裡只做算術。不查 whois、不問 DNS、不測連通性,「全球單播」只代表「沒有被保留給特別用途」,不代表那個位址通。要換算 IPv4 與整數、或做位元檢視,請用 B 抽屜的進位轉換與位元運算工具。