ITエンジニアによるITエンジニアのためのブログ

サブネットの各種プロパティの計算方法

本記事では、インターネット・プロトコル(IP)におけるサブネットの各種プロパティの計算方法について解説します。

ウェブには多数のサブネット計算サイトが存在しますが、本記事を読むことで、それらのサイトが内部でどのような計算を行っているのかがわかるようになります。また、Linuxのifconfigコマンドなどで表示される、ネットワークインターフェース情報も理解できるようになります。

なお、コード例にはRubyを使っていますが、Rubyを知らなくても他の言語で実装できるよう、計算手順は言語に依存しない形で記載しています。

目次

  • CIDRプレフィックスとサブネットマスクの相互変換
    • サブネットマスクからCIDRプレフィックスに変換
    • CIDRプレフィックスからサブネットマスクに変換
  • ネットワークアドレスの計算
  • ブロードキャストアドレスの計算
  • アドレス範囲、アドレス総数、利用可能なホスト数の計算
  • まとめ

CIDRプレフィックスとサブネットマスクの相互変換

CIDRプレフィックスは、172.17.0.1/16のように、IPアドレスの後にスラッシュと数字を追加した表記で、IPアドレスの上位何ビットがネットワークアドレスに該当するかを示しています。

同じように、サブネットマスクは、255.255.0.0のように、32ビットのIPアドレスのうち、どのビットがネットワークアドレスに該当するかを、IPアドレスと同じ表記方法(32ビットを4つのオクテットに分解して10進数に変換した数字をドットで結合)で記載したものです。

どちらもネットワークアドレスに該当するビットの位置を指し示すことには変わりなく、表記方法が違うだけです。

サブネットマスクからCIDRプレフィックスに変換

サブネットマスクからCIDRプレフィックスを計算するには、以下の手順を踏みます。

  1. 4つのオクテットをそれぞれバイナリー変換。
  2. 各バイナリーの中のビットが1の部分を数える。

以下は、この手順をRubyで記載したコードです。

def subnet_mask_to_prefix(subnet_mask)
  subnet_mask.split('.')
             .inject(0) do |sum, octet|
               sum += octet.to_i.to_s(2)
                           .count('1')
              end
end

subnet_mask = '255.255.252.0'
subnet_mask_to_prefix(subnet_mask)
=> 22

例えば、サブネットマスクが255.255.252.0だとすると、CIDRプレフィックスは/22となります。つまり、32ビットのIPアドレスのうち、最初の22ビットがネットワークアドレスであり、残りの10ビットがホストアドレスです。

CIDRプレフィックスからサブネットマスクに変換

逆に、CIDRプレフィックスからサブネットマスクを計算するには以下の手順となります。

  1. プレフィックスからホストビット長を計算(32 – プレフィックス)。
  2. プレフィックスビットには1、ホストビットには0を使った、サブネットマスクと同じビット長(32ビット)の整数を作成する。
  3. 上記整数をサブネットマスク表記に変換。

Rubyでは以下のような形になります。

def prefix_to_subnet_mask(prefix)
  # 1. プレフィックスが正しいか検証。
  unless prefix.between?(0, 32)
    return "エラー: CIDRプレフィックスは0と32の間にしてください。"
  end

  # ホストビット長を計算
  host_bits = 32 - prefix

  # 2. マスク用の整数を作成。
  # 32ビットを全て1で開始 (0xFFFFFFFF)
  # ホストビットを0にするため、その分だけレフトシフト
  mask_integer = 0xFFFFFFFF << host_bits

  # 下位32ビットからはみ出た上位ビットをオフにする。
  mask_integer = mask_integer & 0xFFFFFFFF

  # 3. 32ビット整数をサブネットマスク表記に変換
  octets = [
    (mask_integer >> 24) & 0xFF, # 最初のオクテット
    (mask_integer >> 16) & 0xFF, # 二番目オクテット
    (mask_integer >> 8) & 0xFF,  # 三番目オクテット
    mask_integer & 0xFF          # 四番目オクテット
  ]

  octets.join('.')
end

prefix = 22
prefix_to_subnet_mask(prefix)
=> '255.255.252.0'

コードの最後にmask_integerをライトシフトしてビット単位ANDしているのは、それぞれのオクテットのみを抽出するためです。

例:32ビットのビット配列11111111.11111111.11111100.00000000(ドットは各オクテットを見やすくするため表記)を8ビット分ライトシフトすると、4番目オクテットが消えて3番目オクテットが下位8ビットの数である11111111.11111111.11111100になります。この数と0xFF(8ビットが全て1の数の16進数表記)をビット単位ANDすると、下位8ビットのみの数11111100となり、3番目オクテットが抽出できます。

ネットワークアドレスの計算

ネットワークアドレスは、そのネットワーク全体を指し、特定のデバイスに割り当てることができないIPアドレスです。ネットワークアドレスは、そのネットワークのホストアドレスの範囲内で一番低い数字となります(ホストビットが全て0のため)。

IPアドレスとCIDRプレフィックスもしくはサブネットマスクから、ネットワークアドレスを計算する手順は以下の通りです。

  1. IPアドレスを32ビットの整数に変換。
  2. プレフィックスもしくはサブネットマスクを使って、ネットワークビットが1、ホストビットが0の32ビット整数を作成。
  3. 両者をビット単位ANDして、IPアドレスのホストビットのみをオフ(0)にして、ネットワークアドレスに該当する32ビット整数を作成。
  4. IPアドレス表記に変換。

Rubyでは以下の通りです。

def network_address(ip_address, prefix)
  # 1. IPアドレスを32ビットの整数に変換。
  ip_address_octets = ip_address.split('.')
                                .map(&:to_i)
  ip_address_int = (ip_address_octets[0] << 24) |
                   (ip_address_octets[1] << 16) |
                   (ip_address_octets[2] << 8) |
                   ip_address_octets[3]

  # 2. プレフィックスにより、ネットワークビットが1、ホストビットが0の32ビット整数を作成。
  host_bits = 32 - prefix
  mask_integer = 0xFFFFFFFF << host_bits
  mask_integer = mask_integer & 0xFFFFFFFF

  # 3. 両者をビット単位ANDして、IPアドレスのホストビットのみをオフ(0)にして、ネットワークアドレスに該当する32ビット整数を作成。
  network_integer = ip_address_int & mask_integer

  network_octets = [
    (network_integer >> 24) & 0xFF,
    (network_integer >> 16) & 0xFF,
    (network_integer >> 8) & 0xFF,
    network_integer & 0xFF
  ]

  # 4. IPアドレス表記に変換。
  network_octets.join('.')
end

prefix = 16
ip_address = '172.18.0.1'
network_address(ip_address, prefix)
=> 172.18.0.0

なお、IPアドレスとサブネットマスクの組み合わせによっては、違うサブネットマスクでもネットワークアドレスが同じになる場合があります。

例:IPアドレスが172.18.1.33の場合、サブネットマスクが255.255.252.0でも255.255.0.0でも、ネットワークアドレスは172.18.0.0となる。

このような場合、サブネットマスクの値が大きい方(ホストアドレスの範囲が小さい方)は、サブネットマスクの値が小さい方(ホストアドレスの範囲が大きい方)のサブネットです。

より具体的には、プレフィックス数の差分を2のべき乗とした数のサブネットに分割されています。例えば上記の例だと、255.255.252.0=/22、255.255.0.0=/16なので、/16 のネットワークは、プレフィックス長の差 (22-16=6) を使って2の6乗、すなわち64個の/22のサブネットに分割できます。

このように同じネットワークアドレスを持っていても、プレフィックスによってネットワーク範囲やブロードキャスト・アドレスは当然違ってきますので、ネットワークアドレスのみでそのネットワークを特定することはできません。

ブロードキャストアドレスの計算

ブロードキャストアドレスは、そのネットワーク内の全てのホストに対してデータを送信する際に使われ、特定のデバイスに割り当てることができません。ブロードキャストアドレスには、そのネットワーク内の一番高い値のIPアドレスが割り当てられます。

ブロードキャストアドレスは以下の手順にて求めることができます。

  1. IPアドレスを32ビットの整数に変換。
  2. プレフィックスもしくはサブネットマスクを使って、ネットワークビットが0、ホストビットが1の32ビット整数を作成。
  3. 両者をビット単位ORして、IPアドレスのホストビットを全てオン(1)にして、ブロードキャストアドレスに該当する32ビット整数を作成。
  4. IPアドレス表記に変換。

Rubyでは以下のようになります。

def broadcast_address(ip_address, prefix)
  ip_address_octets = ip_address.split('.')
                                .map(&:to_i)

  # 1. IPアドレスを32ビットの整数に変換。
  ip_address_int = (ip_address_octets[0] << 24) |
                   (ip_address_octets[1] << 16) |
                   (ip_address_octets[2] << 8) |
                   ip_address_octets[3]

  # 2. プレフィックスを使って、ネットワークビットが0、ホストビットが1の32ビット整数を作成。
  mask_integer = 0xFFFFFFFF >> prefix

  # 3. 両者をビット単位ORして、IPアドレスのホストビットを全てオン(1)にして、ブロードキャストアドレスに該当する32ビット整数を作成。
  broadcast_integer = ip_address_int | mask_integer

  broadcast_octets = [
    (broadcast_integer >> 24) & 0xFF,
    (broadcast_integer >> 16) & 0xFF,
    (broadcast_integer >> 8) & 0xFF,
    broadcast_integer & 0xFF
  ]

  broadcast_octets.join('.')
end

ip_address = '172.18.0.1'
prefix = 16
broadcast_address(ip_address, prefix)
 => "172.18.255.255"

なお、ブロードキャストアドレスの第4オクテットの数値は255となることが多いですが、常にそうなるわけではなく、第4オクテットまでネットワークプレフィックスが及んだとき(プレフィックスが25以上)は別の数値になるので注意が必要です。例: 192.168.5.0/26だとブロードキャストアドレスは192.168.5.63になります。)

アドレス範囲、アドレス総数、利用可能なホスト数の計算

あるネットワークにおけるアドレスの範囲は、ネットワークアドレスとブロードキャストアドレスの間となります。

また、このネットワークに存在するアドレスの総数は、ブロードキャストアドレスからネットワークアドレスを引いた値に1を加えることで求められます。

  1. ネットワークアドレスとブロードキャストアドレスをそれぞれ32ビットの整数に変換。
  2. ブロードキャストアドレスからネットワークアドレスを引くと、ネットワークアドレスを除くホストアドレスの個数がわかる。
  3. ネットワークアドレスの分として1を加える。
def number_of_addresses(network_addr, broadcast_addr)
  network_addr_octets = network_addr.split('.')
                                    .map(&:to_i)

  network_addr_int = (network_addr_octets[0] << 24) |
                     (network_addr_octets[1] << 16) |
                     (network_addr_octets[2] << 8) |
                     network_addr_octets[3]

  broadcast_addr_octets = broadcast_addr.split('.')
                                        .map(&:to_i)


  broadcast_addr_int = (broadcast_addr_octets[0] << 24) |
                       (broadcast_addr_octets[1] << 16) |
                       (broadcast_addr_octets[2] << 8) |
                       broadcast_addr_octets[3]


  broadcast_addr_int - network_addr_int + 1
end

例えば、ネットワークアドレスが172.18.0.0/16の場合、ブロードキャストアドレスは172.18.255.255です。このネットワークのアドレスの範囲は172.18.0.0 ~ 172.18.255.255となり、アドレス総数は65536となります。

また、サブネットマスクやプレフィックスを使い、ホストビットの数(=n)を数えて2のn乗することでもホスト数を割り出せます。

  1. 32ビットからプレフィックスを引き、ホストビットの数を計算。
  2. 2をホストビット乗する。
def number_of_addresses(prefix)
  host_bits = 32 - prefix

  2**host_bits
end

ネットワークアドレスとブロードキャストの差分は整数変換する手間がかかるので、こちらのホストビットを数える方が簡単です。

なお、ここで割り出したアドレス総数は、ネットワークアドレスとブロードキャストアドレスを含み、両方とも特定のデバイスに割り当てることはできませんので、実際にデバイスに割り当て可能なホストアドレスの範囲は、(ネットワークアドレス + 1) ~ (ブロードキャストアドレス – 1)となり、利用可能なホスト数はアドレス総数 – 2となります。

例えば、アドレス範囲が172.18.0.0 ~ 172.18.255.255のネットワークなら、利用可能なホストアドレスの範囲は172.18.0.1 ~ 172.18.255.254となり、利用可能なホスト数は65534 (65536 – 2)です。

なお、デフォルトゲートウェイ(ルーター)のアドレスは利用可能なホストアドレスの範囲内ならどのアドレスでも割り当て可能ですが、慣習として最初のアドレス(例:172.18.0.1)か最後のアドレス(例:172.18.255.254)に設定されることが多いです。

まとめ

サブネットは、オフィスネットワークやホームネットワークなどあらゆる場所で利用されており、インターネットで非常に重要な役割を果たしています。

サブネットの各種プロパティの計算方法をコードで実装してみることで、日常的に触れているネットワークの仕組みを理解できるようになり、エンジニアとして業務でネットワークを設定する際にも役に立ちますので、ぜひ試してみてください。