CIDR計算ツール

ネットワークの設定を行っていた際、IPアドレスのCIDR表記(10.0.0.50/28など)が登場して、ぱっと見では実際のIPアドレス範囲を判断できずに難儀しました。
計算サイトはたくさんあるので使えばいいのですが、せっかくなので自分でも計算ツールを作りました。


IPアドレス概要(今回の記事に関わる部分だけの超簡易版)

※注意※
CIDR計算ツールを作ることを主眼に置いているため、IPやネットワークの説明としてはあまり適切でない可能性があります。

IPアドレスを A.B.C.D/E とすると、

  • A~D:第1~第4オクテット
    8bitごとに区切られ、各オクテットは 0 ~ 255 の範囲になります。

  • E:サブネットマスク
    ネットワークの区切り方を示すもので、値は 0 ~ 32 の範囲です。
    何bit目までがマスクされるかを表していて、ネットワーク関係の設定では、255.255.255.0のような形で表記されます。

計算方法について

計算にはサブネットマスクを利用します。

サブネットマスクが 1 ~ 8 、9 ~ 16、17 ~ 24、25 ~ 32 のとき、それぞれ、第1、第2、第3、第4オクテットが計算の対象になります。
※サブネットマスクが 0 の場合はネットワークが区切られないため、計算の必要がありません。

先ほどの例(10.0.0.50/28)の場合、
32-28=4が、サブネットマスクされない残りのbit数であり、
2**4=16が、そのネットワークの中で設定可能なIPの個数となります。
その個数ずつ(=16個ずつ)に、オクテット全体が区切られています。

50は16個ずつ区切りの何番目か計算することで、IPアドレスの範囲を求めることができます。

また、10.0.42.0/22 の場合、
32-22=10が、サブネットマスクされない残りのbit数ですが、
24-22=2が、そのオクテットの中での残りのbit数となり、
2**2=4が、そのオクテットの中で設定可能な値の個数となります。
その個数ずつ(=4個ずつ)に、オクテット全体が区切られています。

42は4個ずつ区切りの何番目か計算することで、IPアドレスの範囲を求めることができます。

CIDR計算ツール

使用言語はPythonです。

import sys

def user_input():
  # CIDRを計算したいIPアドレスを入力
  print("IPアドレスを入力してください。 ex) 192.168.1.1/32")
  input_ip_addr = input()

  # 入力値を各オクテット、サブネットマスクに分割
  sep_input = input_ip_addr.split("/")  
  ip_addr = sep_input[0]
  ip_addr_sep = ip_addr.split(".")
  octets = []
  for i in range(0, 4):
     octets.append(int(ip_addr_sep[i]))
  subnet_mask = int(sep_input[1])

  # 入力値の点検
  check = "true"
  for i in range(0, 4):
    if octets[i] < 0 or octets[i] > 255:
       print("IPアドレスの値が不適切です。")
       check = "false"    
  if subnet_mask < 0 or subnet_mask > 32:
     print("サブネットマスクの値が不適切です。")
     check = "false"
  if check == "false":
     sys.exit()

  # 必要な値を返す
  return subnet_mask, octets


def calc_octet(octets, subnet_mask, oct_num):
   # サブネットマスクの値から、区切られたネットワーク内でのIPの個数を計算
   check_mask = 2 ** ( ( 8 * oct_num ) - subnet_mask )

   # 当該オクテットの値が、サブネットマスクによって区切られたネットワークのうち、何番目に含まれるのか計算
   steps = divmod(octets[oct_num - 1], check_mask)[0]

   # 当該オクテットでのIPアドレス範囲を計算
   oct_start = check_mask * steps
   oct_end = check_mask * ( steps + 1 ) - 1

   # 必要な値を返す
   return oct_start, oct_end


def main(subnet_mask, octets):
  # サブネットマスクが0(=ネットワークを区切らない)の場合
  if subnet_mask == 0:
     print(f"IPアドレス範囲は、0.0.0.0 から 255.255.255.255")

  # サブネットマスク範囲が第1オクテットの場合
  if 1 <= subnet_mask <= 8:
      oct_num = int(1)
      start_and_end = calc_octet(octets, subnet_mask, oct_num)
      print(f"IPアドレス範囲は、{start_and_end[0]}.0.0.0 から {start_and_end[1]}.255.255.255")
  
  # サブネットマスク範囲が第2オクテットの場合
  if 9 <= subnet_mask <= 16:
      oct_num = int(2)
      start_and_end = calc_octet(octets, subnet_mask, oct_num)
      ip_prefix = str(octets[0])
      print(f"IPアドレス範囲は、{ip_prefix}.{start_and_end[0]}.0.0 から {ip_prefix}.{start_and_end[1]}.255.255")
  
  # サブネットマスク範囲が第3オクテットの場合
  if 17 <= subnet_mask <= 24:
      oct_num = int(3)
      start_and_end = calc_octet(octets, subnet_mask, oct_num)
      ip_prefix = str(octets[0]) + "." + str(octets[1])
      print(f"IPアドレス範囲は、{ip_prefix}.{start_and_end[0]}.0 から {ip_prefix}.{start_and_end[1]}.255")
  
  # サブネットマスク範囲が第4オクテットの場合
  if 25 <= subnet_mask <= 32:
      oct_num = int(4)
      start_and_end = calc_octet(octets, subnet_mask, oct_num)
      ip_prefix = str(octets[0]) + "." + str(octets[1]) + "." + str(octets[2])
      print(f"IPアドレス範囲は、{ip_prefix}.{start_and_end[0]} から {ip_prefix}.{start_and_end[1]}")


if __name__ == "__main__":
    # CIDRを計算したいIPアドレスを入力
    user_input = user_input()

    # 入力値の処理結果を変数に格納
    subnet_mask = user_input[0]
    octets = user_input[1]

    # IPアドレス範囲を計算
    main(subnet_mask, octets)

ツール実行結果

例として挙げた2つを実際に計算してみます。

IPアドレスを入力してください。 ex) 192.168.1.1/32
10.0.0.50/28
IPアドレス範囲は、10.0.0.48 から 10.0.0.63
IPアドレスを入力してください。 ex) 192.168.1.1/32
10.0.42.0/22
IPアドレス範囲は、10.0.40.0 から 10.0.43.255

また、ほかのオクテットが対象になる場合なども確認してみます。

IPアドレスを入力してください。 ex) 192.168.1.1/32
10.10.20.20/12
IPアドレス範囲は、10.0.0.0 から 10.15.255.255
IPアドレスを入力してください。 ex) 192.168.1.1/32
10.10.10.10/5
IPアドレス範囲は、8.0.0.0 から 15.255.255.255
IPアドレスを入力してください。 ex) 192.168.1.1/32
10.200.0.0/0
IPアドレス範囲は、0.0.0.0 から 255.255.255.255

インターネットに公開されている計算サイトでの結果と比べてみましたが、問題なく計算できているようです。

余談

Pythonを使うなら、ipaddressモジュールがあるので、わざわざ自前の計算方法を組む必要はない、ということは、後で知りました…


この記事が気に入ったらサポートをしてみませんか?