一覧NW-05-2 · サブネットとアドレス範囲

VLSMによるサブネット分割

一つの/24を用途別に割り当て、残る範囲と別拠点との重複を確認します。

説明

VLSMは用途ごとに異なるプレフィックス長を使う割当て方です。必要数を満たすことに加え、範囲が境界にそろっているか、他の割当てと重ならないか、将来の余地があるかを確認します。

10.24.0.0/24からLAN四つとポイントツーポイント一つへ割り当てます。大きい範囲から確保し、残りを算出します。RFC 1918の範囲と別拠点の計画も照合し、集約経路に含まれる未割当て部分まで確認します。

1必要なアドレス数

LAN-Aは100、LAN-Bは50、LAN-Cは20、管理LANは10アドレスを必要とします。数にはゲートウェイ等の予約も含め、別に対応機器間の二端点リンクを一つ設けます。

import ipaddress; pool = ipaddress.ip_network("10.24.0.0/24")元の範囲; needs = {"LAN-A":100,"LAN-B":50,"LAN-C":20,"management":10}; print(pool,needs)用途別の必要数

/24の総数は256です。通常LANはネットワークとブロードキャストを除いた数で要件を満たす必要があります。二端点リンクは/31を使える条件として別に扱います。

2各LANの最小ブロック幅

必要数に2を加え、それを収める2の累乗を求めます。

sizes = {name: 1 << (count+1).bit_length() for name,count in needs.items()}必要数と予約を収める幅; prefixes = {name: 32-(size.bit_length()-1) for name,size in sizes.items()}幅からプレフィックス長; print([(name,sizes[name],prefixes[name],sizes[name]-2) for name in needs])総数とホスト候補数

順に128個の/25、64個の/26、32個の/27、16個の/28で、通常ホスト候補は126・62・30・14です。必要数の大きい順に確保すると、大きな連続範囲が残らない問題を避けやすくなります。

3大きい範囲から割り当てる

/25を先頭、/26を128、/27を192、/28を224から割り当てます。二端点リンクは残る240から/31とします。

plan = {"LAN-A":ipaddress.ip_network("10.24.0.0/25"),"LAN-B":ipaddress.ip_network("10.24.0.128/26"),"LAN-C":ipaddress.ip_network("10.24.0.192/27"),"management":ipaddress.ip_network("10.24.0.224/28"),"p2p":ipaddress.ip_network("10.24.0.240/31")}境界に沿った割当て; print(*[(name,str(n),str(n.broadcast_address)) for name,n in plan.items()],sep="\n")各範囲の先頭と末尾

各範囲はブロックの境界にそろっています。表示する末尾はIPv4Networkのbroadcast_address属性ですが、/31の二端点リンクでは末尾.241も端点として使います。通常LANのブロードキャスト用途とは分けて扱います。

4範囲外と重複を検査

すべてが元の/24内か、用途同士で重なっていないかを確認します。

from itertools import combinations全組合せを用意; print(all(n.subnet_of(pool) for n in plan.values()))元の範囲内か; print([(a,b) for a,b in combinations(plan,2) if plan[a].overlaps(plan[b])])用途間の重複; remaining = [pool]未使用範囲の初期値

元の範囲内はTrue、重複の組は空です。必要数を満たしていても、同じアドレスを二用途に割り当てる計画は使えません。個数の合計だけでなく位置の重なりを検査します。

5未割当ての範囲

各割当てを元の/24から順に除いて、残りを求めます。

for allocated in plan.values():割当て済みを順に取り出す remaining = [piece for free in remaining for piece in (free.address_exclude(allocated) if allocated.subnet_of(free) else [free])]包含する空き範囲から除く

address_excludeは一つの範囲を取り除いた残りを、複数のCIDRへ分けて返します。ここでは割当てが重ならず、すべてpool内であることを先に確認しているので、その条件に基づいて処理できます。

6空きが14個あっても/28は置けない

残るCIDRと総数を表示し、16アドレス幅の/28が空いているかを確認します。

print([str(n) for n in sorted(remaining)])空きCIDR; print(sum(n.num_addresses for n in remaining))空きの総数; print(any(n.prefixlen <= 28 for n in remaining))/28を置ける範囲があるか

残りは.242/31、.244/30、.248/29の計14個です。/28を追加するには16個の境界にそろった連続範囲が必要で、この計画のままでは入りません。将来増設を見込むなら、必要数を満たすだけでなく空き方も考えます。

7RFC 1918の三範囲との照合

RFC 1918の範囲を明示して、計画がその中にあるかを確認します。

private = [ipaddress.ip_network(n) for n in ("10.0.0.0/8","172.16.0.0/12","192.168.0.0/16")]RFC 1918の三範囲; print(any(pool.subnet_of(n) for n in private))今回の範囲を照合; print(any(ipaddress.ip_address("172.32.0.1") in n for n in private))境界外の例

計画は10/8の中なのでTrue、172.32.0.1は三範囲の外なのでFalseです。172で始まるすべてがプライベート範囲ではありません。またライブラリのis_privateは「RFC 1918の三範囲だけ」と同じ分類とは限らないため、目的に合う条件を明示します。

8接続先拠点と重なっている場合

別拠点が10.24.0.192/26を使っていると分かった場合、どの用途と重なるかを調べます。

remote = ipaddress.ip_network("10.24.0.192/26")接続先の割当て; print([name for name,n in plan.items() if n.overlaps(remote)])重なる用途を特定

LAN-C、management、p2pと重なります。各拠点内で重複していなくても、接続すると同じ宛先を別拠点で使う問題が出ます。プライベート範囲であることは、拠点間で一意に管理されていることの保証ではありません。

9集約経路が含む未割当て部分

拠点全体を10.24.0.0/24で広告すると、未割当ての.250も集約の範囲内かを確認します。

target = ipaddress.ip_address("10.24.0.250")未割当ての宛先; print(target in pool, any(target in n for n in plan.values()))集約と個別割当ての違い

集約には含まれますが、個別の割当てにはありません。範囲をまとめると経路表は小さくできますが、その範囲内の全アドレスに端末が存在する意味ではありません。集約を広告する側で未割当て宛先を適切に処理し、転送ループを作らない設計が必要です。

まとめ

必要数からブロック幅を選び、境界・範囲内・重複を別々に検査します。

空きの総数だけでなく、追加したいCIDRが入る連続範囲を残します。

RFC 1918の所属、別拠点との重複、集約経路の未割当て部分をそれぞれ確認します。

操作履歴と解説を振り返れます。

Python 3

1
import ipaddress; pool = ipaddress.ip_network("10.24.0.0/24"); needs = {"LAN-A":100,"LAN-B":50,"LAN-C":20,"management":10}; print(pool,needs)
10.24.0.0/24 {'LAN-A': 100, 'LAN-B': 50, 'LAN-C': 20, 'management': 10}
2
sizes = {name: 1 << (count+1).bit_length() for name,count in needs.items()}; prefixes = {name: 32-(size.bit_length()-1) for name,size in sizes.items()}; print([(name,sizes[name],prefixes[name],sizes[name]-2) for name in needs])
[('LAN-A', 128, 25, 126), ('LAN-B', 64, 26, 62), ('LAN-C', 32, 27, 30), ('management', 16, 28, 14)]
3
plan = {"LAN-A":ipaddress.ip_network("10.24.0.0/25"),"LAN-B":ipaddress.ip_network("10.24.0.128/26"),"LAN-C":ipaddress.ip_network("10.24.0.192/27"),"management":ipaddress.ip_network("10.24.0.224/28"),"p2p":ipaddress.ip_network("10.24.0.240/31")}; print(*[(name,str(n),str(n.broadcast_address)) for name,n in plan.items()],sep="\n")
('LAN-A', '10.24.0.0/25', '10.24.0.127')
('LAN-B', '10.24.0.128/26', '10.24.0.191')
('LAN-C', '10.24.0.192/27', '10.24.0.223')
('management', '10.24.0.224/28', '10.24.0.239')
('p2p', '10.24.0.240/31', '10.24.0.241')
4
from itertools import combinations; print(all(n.subnet_of(pool) for n in plan.values())); print([(a,b) for a,b in combinations(plan,2) if plan[a].overlaps(plan[b])]); remaining = [pool]
True
[]
5
for allocated in plan.values(): remaining = [piece for free in remaining for piece in (free.address_exclude(allocated) if allocated.subnet_of(free) else [free])]
6
print([str(n) for n in sorted(remaining)]); print(sum(n.num_addresses for n in remaining)); print(any(n.prefixlen <= 28 for n in remaining))
['10.24.0.242/31', '10.24.0.244/30', '10.24.0.248/29']
14
False
7
private = [ipaddress.ip_network(n) for n in ("10.0.0.0/8","172.16.0.0/12","192.168.0.0/16")]; print(any(pool.subnet_of(n) for n in private)); print(any(ipaddress.ip_address("172.32.0.1") in n for n in private))
True
False
8
remote = ipaddress.ip_network("10.24.0.192/26"); print([name for name,n in plan.items() if n.overlaps(remote)])
['LAN-C', 'management', 'p2p']
9
target = ipaddress.ip_address("10.24.0.250"); print(target in pool, any(target in n for n in plan.values()))
True False

演習完了