CIDR プレフィックスからネットマスクを作る
設定に 192.168.0.0/16 と書いたとき、ある IP がこの範囲に入っているかを、誰がどう判定しているのでしょうか。答えはビット演算 1 回です。その道具になるのがネットマスクです。
この IP は、この範囲に入っているのか
/16 や /24 は CIDR と呼ばれる書き方で、32 ビットのうち上から何ビットがネットワークを表すかを示します。/24 なら、上の 24 ビットが同じ相手は同じネットワークにいて、残りの 8 ビットが個々の機器の番号です。/32 は 1 台だけを指し、/0 はすべての IP を指します。
昔はこれを 255.255.255.0 のように点で区切って書いていましたが、いまは /24 の形で書くのが普通です。書き方が変わっただけで、指しているものは同じです。
上から n ビットだけ 1 にする
ネットマスクは、ネットワーク側のビットを 1、機器側のビットを 0 にした 32 ビットの値です。/24 なら 16 進で FF FF FF 00 と並びます。上の 24 個が 1、下の 8 個が 0、という並びがそのまま見て取れます。
プレーンテキスト
/24 のネットマスク
11111111 11111111 11111111 00000000
FF FF FF 00作るときの勘所は、1 をどこまで並べるかです。下から数えて何ビットを 0 にするか、と裏返して考えると、32 - n という数が出てきます。
似た形で、下のほうだけを 1 にしたいこともあります。ハッシュ値を 2 の n 乗個の入れ物に振り分けるときなどです。
Python
n = 4
low_mask = (1 << n) - 1 # 0b1111
print(hash("key") & low_mask) # 0 から 15 のどれか1 << n は 1 を n 個ぶん左へ動かした値で、そこから 1 を引くと下の n ビットがすべて 1 になります。ネットマスクは、これの上下が入れ替わった形です。
注意が 1 つあります。n が端の値のとき、ずらす量が 32 になります。32 ビットの値を 32 ビットずらす動きは言語ごとに違い、0 になるものも、元のままのものもあります。両端は先に切り分けておくと安全です。
作った値が正しいかどうかは、16 進で書き出して並びを見るのが早いです。1 の列と 0 の列の境目が、n 個目のところに来ていれば合っています。下のほうが 1 になっていたら、上下が逆さまです。
下位を捨ててから比べる
マスクさえ作れれば、範囲の判定は AND 1 回で終わります。
Python
mask = 0xFFFFFF00 # /24
ip = 0xC0A8012A # 192.168.1.42
net = 0xC0A80100 # 192.168.1.0
print(ip & mask == net & mask) # TrueAND を取ると、マスクが 0 の位置は問答無用で 0 になります。つまり機器の番号にあたる部分が消えて、ネットワークの部分だけが残ります。残った部分どうしが一致すれば、同じネットワークです。ルーターが行き先を選ぶときの最長一致も、プレフィックスの長い順にこの比較を繰り返しているだけです。
要件
- 上位 n bit が 1、下位 (32 - n) bit が 0 の 32bit 整数を返す
- n=0 のとき 0、n=32 のとき 4294967295 を返す
- n の範囲は 0 から 32
入出力例
cidrMask(0) → 0
cidrMask(8) → 4278190080
cidrMask(16) → 4294901760
cidrMask(24) → 4294967040
cidrMask(32) → 4294967295
cidrMask(1) → 2147483648
cidrMask(4) → 4026531840
cidrMask(28) → 4294967280