နားလည်ထားရမယ့် အချက်
Subnet calculator ဟာ network engineer အများစု ပထမဆုံး ရေးဖြစ်တဲ့ tool ပါ။ ဒါက project အနေနဲ့ ကောင်းတဲ့ အကြောင်းရင်းက addressing theory တိုင်းကို အမှန်တကယ် run လို့ရတဲ့ code အဖြစ် ပြောင်းခိုင်းလိုက်လို့ပါ။ အရင်က သင်ခဲ့တဲ့ concept လေးခု ဒီမှာ ဆုံဆည်းပါတယ်။ ပထမတစ်ခုက IPv4 address ဟာ decimal လေးလုံး မဟုတ်ဘဲ bit ၃၂ လုံး ဖြစ်တယ်ဆိုတဲ့ အချက်ပါ — ipaddress module က arithmetic ကို ဖုံးကွယ်ပေးထားပေမယ့်၊ bit တွေကို စိတ်ထဲ မြင်ထားမှ ဒီ program တစ်ခုလုံး အဓိပ္ပာယ်ရှိပါလိမ့်မယ်။ ဒုတိယက subnet mask ဟာ network portion နဲ့ host portion ကြားက နယ်နိမိတ် ဖြစ်တယ်ဆိုတာပါ — base.subnets(new_prefix=...) ဆိုတာ အဲဒီ နယ်နိမိတ်ကို ညာဘက်ကို ရွှေ့လိုက်ရုံပါပဲ။ တတိယက bit borrowing rule ပါ — block တစ်ခုကို အနည်းဆုံး N ပိုင်း ခွဲဖို့ 2^n >= N ဖြစ်စေတဲ့ အသေးဆုံး n ကို ယူရပါတယ်၊ ဒါကို bits_to_borrow() က တွက်ပေးတယ်။ Subnet 4 ခု တောင်းရင် 2 bit ချေးရမယ်၊ 5 ခု တောင်းရင်တောင် 3 bit ချေးရမယ်၊ ဘာလို့လဲဆိုတော့ subnetting ဟာ 2 ၏ ထပ်ကိန်းအလိုက်သာ ခွဲလို့ရလို့ပါ။ ကျန်တဲ့ subnet တွေက အသုံးမပြုရသေးတဲ့ capacity ပါ။ စတုတ္ထက 2^n - 2 usable-host rule နဲ့၊ အရေးကြီးဆုံးအနေနဲ့ အဲဒီ rule ဘယ်နေရာမှာ မမှန်တော့လဲဆိုတာပါ။
နောက်ဆုံး အချက်ကြောင့်ပဲ ဒီ program မှာ တစ်ကြောင်းတည်း အနုတ်သင်္ချာ မဟုတ်ဘဲ usable_range() ဆိုတဲ့ function သီးသန့် ထားရတာပါ။ ပုံမှန် subnet မှာ all-zeros address က network ကို ကိုယ်စားပြုပြီး all-ones address က broadcast ဖြစ်လို့ နှစ်ခုစလုံး မသုံးလို့ရပါဘူး။ ဒါပေမယ့် /31 မှာ address နှစ်ခုပဲ ရှိပြီး RFC 3021 က ပြန်သတ်မှတ်ထားတယ် — point-to-point link မှာ broadcast မလိုတော့လို့ နှစ်ဖက်စလုံး သုံးလို့ရတယ်။ /32 ကတော့ host route တစ်ခုတည်း — address တစ်ခုတည်း၊ network/broadcast အတွဲ လုံးဝ မရှိပါဘူး။ ရိုးရိုး calculator တစ်ခုက အဲဒီ case နှစ်ခုအတွက် usable host 0 နဲ့ -1 လို့ ထုတ်ပေးမှာပါ။ ဒါတွေကို မှန်မှန်ကန်ကန် ကိုင်တွယ်နိုင်မှ ကစားစရာ tool တစ်ခုနဲ့ address plan တစ်ခုလုံးကို အပ်နှံလို့ရတဲ့ tool တစ်ခု ကွာခြားသွားမှာပါ။
SUBNET CALCULATOR DATA FLOW
---------------------------
INPUT PROCESSING OUTPUT
----- ---------- ------
+---------------+
| 192.168.10.0 |
| /24 |---+
+---------------+ |
v
+---------------+ +------------------------------+
| need 4 subnets|->| bits_to_borrow: 2^n >= 4 |
+---------------+ | n = 2 bits borrowed|
+------------------------------+
|
v
+------------------------------+
| new prefix = /24 + 2 = /26 |
| base.subnets(new_prefix=26) |
+------------------------------+
|
v
+------------------------------+ +------------+
| usable_range() per subnet | | aligned |
| /32 -> 1 (host route) |---->| allocation |
| /31 -> 2 (RFC 3021) | | table |
| else -> 2^n - 2 | +------------+
+------------------------------+လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
Address planning ဟာ အမှားလုပ်မိရင် ကုန်ကျစရိတ် ကြီးပြီး တိတ်တဆိတ် ဖြစ်တတ်တဲ့ network task နည်းနည်းလေးထဲက တစ်ခုပါ။ Environment အသစ်တစ်ခုအတွက် 10.20.0.0/16 ကို လက်ခံရပြီး availability zone သုံးခုအတွက်၊ တစ်ခုချင်းစီမှာ public, private, database tier တွေအတွက် subnet ခွဲပေးဖို့ တာဝန်ကျတယ် ဆိုပါစို့။ Spreadsheet နဲ့ လက်နဲ့ တွက်ရင် ဖြစ်တတ်တဲ့ ပြဿနာက broadcast address မှားတွက်မိတာ မဟုတ်ပါဘူး — block နှစ်ခု တိတ်တဆိတ် overlap ဖြစ်နေတာ၊ ဒါမှမဟုတ် နယ်နိမိတ်တစ်ခုက 2 ၏ ထပ်ကိန်း အစွန်းမှာ မကျဘဲ router က ဘေးက range ကို မျိုချသွားတဲ့ summary route ကို တိတ်တဆိတ် လက်ခံလိုက်တာမျိုးပါ။ ဘာ error မှ မတက်ပါဘူး။ လအတန်ကြာမှ traffic တွေ လွဲနေတာကို တွေ့ရမယ်၊ အများအားဖြင့် အဲဒီ VPC ကို တခြား VPC နဲ့ peer လုပ်တဲ့ အချိန်ကျမှပါ။
ဒီလို tool တစ်ခုက plan ကို ပြန်စစ်လို့ရအောင် လုပ်ပေးပါတယ်။ နယ်နိမိတ်တိုင်းကို ကိုယ်တိုင် ရိုက်ထည့်တဲ့ arithmetic ကနေ မဟုတ်ဘဲ base.subnets() ကနေ ထုတ်ယူတဲ့အတွက် overlap ဖြစ်ဖို့ လုံးဝ မဖြစ်နိုင်တော့ပါဘူး၊ ပြီးတော့ ထုတ်ပေးတဲ့ table က လုပ်ဖော်ကိုင်ဖက်တစ်ယောက် စက္ကန့် ၁၀ အတွင်း စစ်လို့ရပါတယ်။ ဒုတိယ အကျိုးကျေးဇူးက sizing အကြောင်း ဆွေးနွေးနိုင်ဖို့ပါ။ Subnet 5 ခု တောင်းလိုက်ရင် 3 bit ချေးပြီး 8 ခု ပြပေးမယ် — နောင်လိုချင်မယ့် capacity ကို ဒီအခုပဲ ကုန်ခံနေတာလားဆိုတာ ချက်ချင်း မြင်ရမယ်။ /24 တစ်ခုကနေ 4 ခု တောင်းရင် tier တစ်ခုစီမှာ host 62 လုံး ရမယ်၊ တစ်ချိန်ချိန်မှာ ဒီထက်ပိုမယ်ဆိုရင် scaling ဖြစ်တဲ့ အချိန်ကျမှ မဟုတ်ဘဲ အခုပဲ သိရမယ်။ /31 row ကလည်း လက်တွေ့မှာ အရေးပါတယ် — router တွေကြားက point-to-point link တွေဟာ များသောအားဖြင့် /31 ဒါမှမဟုတ် /30 ဖြစ်ပြီး၊ /31 ကို "usable host 0" လို့ ပြတဲ့ calculator က မရှိတဲ့ bug တစ်ခုကို လူတစ်ယောက် လိုက်ရှာနေအောင် လုပ်ပစ်လိမ့်မယ်။
အတူတူ စမ်းရေးကြည့်မယ်
import ipaddress
def bits_to_borrow(required):
"""Smallest n such that 2**n >= required subnets."""
n = 0
while (1 << n) < required:
n += 1
return n
def usable_range(net):
"""First host, last host, usable count -- honouring /31 and /32."""
if net.prefixlen == net.max_prefixlen:
# /32: one address, a host route. No network/broadcast split at all.
return net.network_address, net.network_address, 1
if net.prefixlen == net.max_prefixlen - 1:
# /31: RFC 3021 point-to-point link. Both addresses are usable.
return net.network_address, net.broadcast_address, 2
# Normal case: drop the network address and the broadcast address.
return net.network_address + 1, net.broadcast_address - 1, net.num_addresses - 2
def allocate(cidr, required):
base = ipaddress.ip_network(cidr)
borrow = bits_to_borrow(required)
new_prefix = base.prefixlen + borrow
if new_prefix > base.max_prefixlen:
raise ValueError("cannot fit %d subnets inside %s" % (required, base))
return base, borrow, new_prefix, list(base.subnets(new_prefix=new_prefix))
def print_plan(cidr, required):
base, borrow, new_prefix, subnets = allocate(cidr, required)
print("SUBNET ALLOCATION PLAN")
print("=" * 72)
print("Base block : %s" % base)
print("Base netmask : %s" % base.netmask)
print("Subnets needed : %d" % required)
print("Bits borrowed : %d (2^%d = %d subnets)" % (borrow, borrow, 1 << borrow))
print("New prefix : /%d (netmask %s)" % (new_prefix, subnets[0].netmask))
print("-" * 72)
print("%-3s %-18s %-15s %-15s %s" % ("#", "NETWORK", "FIRST HOST", "LAST HOST", "BROADCAST"))
print("-" * 72)
for i, net in enumerate(subnets, start=1):
first, last, _ = usable_range(net)
bcast = "n/a" if net.prefixlen >= net.max_prefixlen - 1 else str(net.broadcast_address)
print("%-3d %-18s %-15s %-15s %s" % (i, net.with_prefixlen, first, last, bcast))
print("-" * 72)
print("Usable hosts per subnet: %d" % usable_range(subnets[0])[2])
print()
def print_edge_cases():
print("EDGE CASES: WHERE 2^n - 2 STOPS BEING TRUE")
print("=" * 72)
print("%-16s %-8s %-15s %-15s %s" % ("BLOCK", "ADDRS", "FIRST HOST", "LAST HOST", "USABLE"))
print("-" * 72)
for cidr in ["10.0.0.0/29", "10.0.0.0/30", "10.0.0.8/31", "10.0.0.12/32"]:
net = ipaddress.ip_network(cidr)
first, last, count = usable_range(net)
print("%-16s %-8d %-15s %-15s %d" % (net.with_prefixlen, net.num_addresses, first, last, count))
print("-" * 72)
print("A /31 has no broadcast address (RFC 3021), so both addresses are")
print("usable. A /32 is a single host route. Blindly applying 2^n - 2")
print("would report 0 and -1 usable hosts for these two cases.")
print_plan("192.168.10.0/24", 4)
print_edge_cases()
SUBNET ALLOCATION PLAN
========================================================================
Base block : 192.168.10.0/24
Base netmask : 255.255.255.0
Subnets needed : 4
Bits borrowed : 2 (2^2 = 4 subnets)
New prefix : /26 (netmask 255.255.255.192)
------------------------------------------------------------------------
# NETWORK FIRST HOST LAST HOST BROADCAST
------------------------------------------------------------------------
1 192.168.10.0/26 192.168.10.1 192.168.10.62 192.168.10.63
2 192.168.10.64/26 192.168.10.65 192.168.10.126 192.168.10.127
3 192.168.10.128/26 192.168.10.129 192.168.10.190 192.168.10.191
4 192.168.10.192/26 192.168.10.193 192.168.10.254 192.168.10.255
------------------------------------------------------------------------
Usable hosts per subnet: 62
EDGE CASES: WHERE 2^n - 2 STOPS BEING TRUE
========================================================================
BLOCK ADDRS FIRST HOST LAST HOST USABLE
------------------------------------------------------------------------
10.0.0.0/29 8 10.0.0.1 10.0.0.6 6
10.0.0.0/30 4 10.0.0.1 10.0.0.2 2
10.0.0.8/31 2 10.0.0.8 10.0.0.9 2
10.0.0.12/32 1 10.0.0.12 10.0.0.12 1
------------------------------------------------------------------------
A /31 has no broadcast address (RFC 3021), so both addresses are
usable. A /32 is a single host route. Blindly applying 2^n - 2
would report 0 and -1 usable hosts for these two cases.
၅ မိနစ် စမ်းကြည့်
Program ကို subnet အရေအတွက် အစား "subnet တစ်ခုစီမှာ လိုအပ်တဲ့ host အရေအတွက်" ကနေလည်း တွက်လို့ရအောင် တိုးချဲ့ကြည့်ပါ။ hosts_to_prefix(base, hosts_needed) function တစ်ခု ရေးပြီး၊ 2^h - 2 >= hosts_needed ဖြစ်စေမယ့် အသေးဆုံး host bit h ကို ရှာပါ၊ ပြီးရင် new_prefix = 32 - h လို့ တွက်ပါ။ 192.168.10.0/24 ကို subnet တစ်ခုစီ host 30 လုံးနဲ့ ခွဲကြည့်ပါ — /27 ရမယ်၊ subnet 8 ခုနဲ့ usable 30 လုံးစီ ရပါလိမ့်မယ်။ ပြီးရင် host 300 လုံး တောင်းကြည့်ပါ — /24 ထဲ မဆံ့တော့လို့ error တက်ရမယ်။ အဲဒီ case ကို သေချာ ကိုင်တွယ်ပါ။
သတိလေးတစ်ချက်
2^n - 2 ကို ခြွင်းချက်မရှိ အမြဲသုံးမိတာ။ /31 အတွက် usable host 0 လို့ ပြပြီး /32 အတွက် -1 လို့ ပြမှာဖြစ်လို့၊ မရှိတဲ့ bug တစ်ခုကို address plan ထဲ လိုက်ရှာနေရပါလိမ့်မယ်။ Point-to-point link တွေဟာ /31 နဲ့ /30 က ပုံမှန်ဖြစ်လို့ ဒီ case ဟာ လက်တွေ့အလုပ်မှာ မကြာခင် တွေ့ရပါလိမ့်မယ် — max_prefixlen နဲ့ သူ့အနီးက prefix တွေအတွက် branch သီးသန့် ထားပေးပါ။
Subnet အရေအတွက်နဲ့ ချေးရမယ့် bit အရေအတွက်ကို မှားရောနေတာ။ Subnet 5 ခု တောင်းတာဟာ 5 bit ချေးတာ မဟုတ်ပါဘူး၊ 3 bit ပဲ ချေးရပါတယ်၊ ဘာလို့လဲဆိုတော့ subnetting က 2 ၏ ထပ်ကိန်းအလိုက်ပဲ ခွဲလို့ရလို့ပါ။ new_prefix = base.prefixlen + bits_to_borrow(required) အစား + required လို့ ရေးမိရင် ကြည့်လို့ ကောင်းပေမယ့် လုံးဝ မှားနေတဲ့ plan ထွက်လာမယ် — /24 ကို "5 ပိုင်း" ခွဲတာ /29 subnet 32 ခု ဖြစ်သွားပါလိမ့်မယ်။
RFC 3021 -- Using 31-Bit Prefixes on IPv4 Point-to-Point Links — Computer Networking