IPv4プレフィックスとアドレス範囲
同じIPのプレフィックス長を変え、所属範囲と端の扱いを確認します。
説明
IPv4のプレフィックス長は、32ビットのうちネットワークを示す先頭部分の長さです。アドレスの見た目が近くても、マスクによって同じ範囲かどうかが変わります。
192.0.2.99/26をipaddressで解析し、マスク、範囲の先頭と末尾、利用するホストの範囲を確認します。/25・/27へ変えた場合を比較し、最後に通常のLANとは数え方が異なる/31と/32を扱います。
1端末アドレスから所属範囲を得る
ip_interfaceは端末のIPとマスクを組として扱います。まず192.0.2.99/26を解析します。
import ipaddressアドレス操作を読み込む; interface = ipaddress.ip_interface("192.0.2.99/26")端末の設定; net = interface.network; print(interface.ip, net, net.netmask)IP・範囲・マスク所属範囲は192.0.2.64/26、マスクは255.255.255.192です。.99を先頭とする範囲ではありません。/26では末尾6ビットがホスト部分なので、範囲の幅は64アドレスです。
2マスクのAND演算を確認
IPとマスクを32ビットの整数へ変換し、ANDでネットワーク部分を残します。
address = int(interface.ip); mask = int(net.netmask)32ビット整数へ変換; print(f"{address:032b}"); print(f"{mask:032b}")同じ桁数で比較; print(ipaddress.IPv4Address(address & mask))ネットワーク部分を残すマスクは先頭26ビットが1、残る6ビットが0です。最後のオクテットでは99=01100011と192=11000000のANDが64=01000000になり、所属範囲の先頭が得られます。
3先頭・末尾・総数
ネットワークアドレスとブロードキャストアドレス、含まれる全アドレス数を表示します。
print(net.network_address, net.broadcast_address, net.num_addresses)範囲の両端と総数先頭.64、末尾.127、総数64です。通常のこのLANでは.64はネットワーク、.127はブロードキャストに使い、端末へ割り当てる範囲はその間になります。
4ホストとして列挙される範囲
hostsで通常のホスト範囲を取り出し、先頭と末尾と数を確認します。
hosts = list(net.hosts())ホスト範囲を列挙; print(hosts[0], hosts[-1], len(hosts))割当て候補の両端と数192.0.2.65〜126の62アドレスです。ゲートウェイ、冗長化用の仮想IP、固定管理用の予約もこの中を使うなら、その分を端末の必要数へ含めます。62台の一般端末に加えて別のIPを無制限に割り当てられるわけではありません。
5サブネットに含まれるアドレスと端末に割り当てられるアドレス
.63・.64・.99・.127・.128を、範囲への所属と通常のホスト候補の両方で調べます。
candidates = [ipaddress.ip_address("192.0.2." + str(n)) for n in (63,64,99,127,128)]境界の前後と内部; print([(str(ip), ip in net, ip in hosts) for ip in candidates])所属と割当て候補を比較.64と.127は範囲内ですが、この通常LANのホスト候補ではありません。.63と.128は隣の範囲です。範囲検査のTrueだけを、端末へ割当て可能という判定に使わないようにします。
6同じIPのプレフィックス長を変える
IPを.99に保ち、/25・/26・/27で所属範囲を作り直します。
variants = [ipaddress.ip_interface(f"192.0.2.99/{p}").network for p in (25,26,27)]同じIPで三種類のマスク; print([(str(n),str(n.broadcast_address),n.num_addresses) for n in variants])変化した範囲と数/25は.0〜127の128個、/26は.64〜127の64個、/27は.96〜127の32個です。末尾が同じでも先頭と数が変わります。プレフィックスが1ビット長くなると範囲の幅が半分になります。
7ポイントツーポイントの/31
対応機器同士のポイントツーポイントリンクで192.0.2.10/31を使う場合を確認します。
link = ipaddress.ip_network("192.0.2.10/31")二端点の範囲; print(link.num_addresses, [str(ip) for ip in link.hosts()])総数と端点総数2で、.10と.11の両方を端点として使えます。RFC 3021の/31では通常のLANのように両端を除外しません。どの接続でも無条件にこの扱いにできるのではなく、リンクと機器の対応条件を確認します。
8一つのアドレスを表す/32
一つだけを示すホスト経路の範囲を作ります。
single = ipaddress.ip_network("192.0.2.99/32")単一アドレスの範囲; print(single.num_addresses, [str(ip) for ip in single.hosts()], ipaddress.ip_address("192.0.2.100") in single)一致する一つだけを確認含むのは.99一つで、.100は対象外です。/32のホスト経路やループバック等の用途では、通常LANの総数から2を引く計算を使いません。アドレス数はプレフィックスだけでなく、どの用途の利用可能数を数えるのかも明示します。
まとめ
プレフィックス長とマスクから、範囲の先頭・末尾・総数が決まります。
範囲内であることと、通常LANのホストへ割り当てられることは別です。
/31のポイントツーポイントや/32では、単純に総数から2を引く扱いにしません。
操作履歴と解説を振り返れます。
Python 3
import ipaddress; interface = ipaddress.ip_interface("192.0.2.99/26"); net = interface.network; print(interface.ip, net, net.netmask)192.0.2.99 192.0.2.64/26 255.255.255.192
address = int(interface.ip); mask = int(net.netmask); print(f"{address:032b}"); print(f"{mask:032b}"); print(ipaddress.IPv4Address(address & mask))11000000000000000000001001100011 11111111111111111111111111000000 192.0.2.64
print(net.network_address, net.broadcast_address, net.num_addresses)192.0.2.64 192.0.2.127 64
hosts = list(net.hosts()); print(hosts[0], hosts[-1], len(hosts))192.0.2.65 192.0.2.126 62
candidates = [ipaddress.ip_address("192.0.2." + str(n)) for n in (63,64,99,127,128)]; print([(str(ip), ip in net, ip in hosts) for ip in candidates])[('192.0.2.63', False, False), ('192.0.2.64', True, False), ('192.0.2.99', True, True), ('192.0.2.127', True, False), ('192.0.2.128', False, False)]variants = [ipaddress.ip_interface(f"192.0.2.99/{p}").network for p in (25,26,27)]; print([(str(n),str(n.broadcast_address),n.num_addresses) for n in variants])[('192.0.2.0/25', '192.0.2.127', 128), ('192.0.2.64/26', '192.0.2.127', 64), ('192.0.2.96/27', '192.0.2.127', 32)]link = ipaddress.ip_network("192.0.2.10/31"); print(link.num_addresses, [str(ip) for ip in link.hosts()])2 ['192.0.2.10', '192.0.2.11']
single = ipaddress.ip_network("192.0.2.99/32"); print(single.num_addresses, [str(ip) for ip in single.hosts()], ipaddress.ip_address("192.0.2.100") in single)1 ['192.0.2.99'] False
演習完了