プライベートIPアドレスの範囲をリアルな縮尺で示した図を作った

# プライベートIPアドレスの範囲を図で示す

IPv4では、全アドレス空間`0.0.0.0～255.255.255.255`のうち以下のa,b,cの範囲をプライベートIPアドレスとして使用します:

a. 10.0.0.0 ～ 10.255.255.255
b. 172.16.0.0 ～ 172.31.255.255
c. 192.168.0.0 ～ 192.168.255.255

これらの範囲を**模式的に**図示するとこんな感じです:
![privateIP_moshiki.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150194/20973824-5e13-24c7-7f57-c271b3d0e115.png)
「グレーの範囲がプライベートIPの範囲です」と言われれば、そうですねと答えるしかない図ですが、しかしこの図は誇張しすぎです。グレーの範囲はもっと狭いはずです。

どういうことかというと、すべてのアドレスの数は$2^{32}$ = 4,294,967,296通り（約42億）で、実際のところ何%をプライベートIPアドレスが占めるのかを計算してみると、

a. 10.0.0.0 ～ 10.255.255.255 → $2^{24}$通りなので、$2^{24}/2^{32} \\,\\,\fallingdotseq \\,\\,0.3906\\%$
b. 172.16.0.0 ～ 172.31.255.255 → $2^{20}$通りなので、$2^{20}/2^{32} \\,\\,\fallingdotseq \\,\\,0.0244\\%$
c. 192.168.0.0 ～ 192.168.255.255 → $2^{16}$通りなので、$2^{16}/2^{32} \\,\\,\fallingdotseq \\,\\,0.0015\\%$

a,b,cの合計でも$0.4165\\%$です。少ない。

この比率をリアルに反映させたら図はどうなるの、と思い付いてしまったので作ってみました。こうなりました:
![privateIP_real_monochrome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150194/237d2d1e-88a2-98dd-0c66-1022ffed60c0.png)
予想した通りスカスカになりました。満足です。a,b,cの範囲はいずれも矩形で描いてるんですが、bとcは狭すぎてほぼ線になっちゃいましたね。

需要のほどはわかりませんが使いどころがあれば図はよしなにご利用ください。カラー版も置いておきます。
![privateIP_real_color.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150194/adf4315f-249b-71cd-c6ea-0946f44aeed4.png)


# コード

図中の数字はパワポで書き加えましたが、中核になる図はPythonとOpenCVで作りました。

IPアドレスを数値にするところがちょっと面倒かなと思ったのですが、[ipaddress --- IPv4/IPv6 操作ライブラリ](https://docs.python.org/ja/3/library/ipaddress.html) というまさになライブラリがあったので使いました。こんな感じでint()をかませば数値にしてくれます。

```Python3
import ipaddress
ipaddr = ipaddress.IPv4Address('192.168.0.0')
print(ipaddr) # 192.168.0.0
print(int(ipaddr)) # 3232235520
```

あとは、OpenCVで矩形を描くrectangle()と直線を描くline()を使って、こんな感じのコードになりました:

```Python3
import cv2
import numpy as np
import ipaddress

height = 600
width = 3000
x_offset = 250
y_offset = 150
ip_box_length = width - 2 * x_offset
img = np.full((height, width, 3), (255, 255, 255), dtype=np.uint8)

IP_MAX = ipaddress.IPv4Address('255.255.255.255')
ip_head_a = ipaddress.IPv4Address('10.0.0.0')
ip_tail_a = ipaddress.IPv4Address('10.255.255.255')
ip_head_b = ipaddress.IPv4Address('172.16.0.0')
ip_tail_b = ipaddress.IPv4Address('172.31.255.255')
ip_head_c = ipaddress.IPv4Address('192.168.0.0')
ip_tail_c = ipaddress.IPv4Address('192.168.255.255')

x_head_a = x_offset + (int(ip_head_a) / int(IP_MAX)) * ip_box_length
x_tail_a = x_offset + (int(ip_tail_a) / int(IP_MAX)) * ip_box_length
x_head_b = x_offset + (int(ip_head_b) / int(IP_MAX)) * ip_box_length
x_tail_b = x_offset + (int(ip_tail_b) / int(IP_MAX)) * ip_box_length
x_head_c = x_offset + (int(ip_head_c) / int(IP_MAX)) * ip_box_length
x_tail_c = x_offset + (int(ip_tail_c) / int(IP_MAX)) * ip_box_length

cv2.rectangle(img, (x_offset, y_offset), (width-x_offset, height-y_offset), (0, 0, 0), thickness=2)
cv2.rectangle(img, (round(x_head_a), y_offset), (round(x_tail_a), height-y_offset), (0, 0, 255), thickness=-1)
cv2.rectangle(img, (round(x_head_b), y_offset), (round(x_tail_b), height-y_offset), (0, 255, 0), thickness=-1)
cv2.rectangle(img, (round(x_head_c), y_offset), (round(x_tail_c), height-y_offset), (255, 0, 0), thickness=-1)

ip_64 = ipaddress.IPv4Address('64.0.0.0')
ip_128 = ipaddress.IPv4Address('128.0.0.0')
ip_192 = ipaddress.IPv4Address('192.0.0.0')

x_64  = x_offset + (int(ip_64) / int(IP_MAX)) * ip_box_length
x_128  = x_offset + (int(ip_128) / int(IP_MAX)) * ip_box_length
x_192  = x_offset + (int(ip_192) / int(IP_MAX)) * ip_box_length

cv2.line(img, (round(x_64), y_offset), (round(x_64), height-y_offset), (128, 128, 128))
cv2.line(img, (round(x_128), y_offset), (round(x_128), height-y_offset), (128, 128, 128))
cv2.line(img, (round(x_192), y_offset), (round(x_192), height-y_offset), (128, 128, 128))

cv2.imwrite('C:\\tmp\\privateIP.png', img)
```