Python collapse_addresses: รวม CIDR ที่ติดกันโดยไม่ขยายเป็นทุก IP
ACL ที่สะสมมานานมักมี CIDR ซ้อนกันหรือมีสองครึ่งที่รวมเป็น network ใหญ่ได้ครับ ipaddress.collapse_addresses รับ network/address iterable แล้วคืนชุดที่ครอบคลุมพื้นที่เดิมโดยตัดส่วนซ้ำและรวมช่วงที่ติดกันได้
WHY: ลดกฎโดยไม่แจกแจงทุก address
สอง /25 ที่เรียงติดและอยู่บน boundary เดียวกันรวมเป็น /24 ได้ เหมือนรวมโต๊ะสองแถวเป็นโซนเดียว แต่ช่วงที่ดูติดกันด้วยตาอาจรวมไม่ได้หาก prefix alignment ไม่พอดี ฟังก์ชันช่วยทำ binary boundary ให้ถูกโดยไม่สร้าง IP ทุกตัวในหน่วยความจำ
from ipaddress import IPv4Network, IPv6Network, collapse_addresses
import unittest
def compact_ipv4(values):
networks = [IPv4Network(value) for value in values]
return [str(network) for network in collapse_addresses(networks)]
class CollapseTests(unittest.TestCase):
def test_adjacent_halves_become_one_network(self):
self.assertEqual(
compact_ipv4(["10.0.0.0/25", "10.0.0.128/25", "10.0.0.0/26"]),
["10.0.0.0/24"],
)
def test_disjoint_networks_remain_separate(self):
self.assertEqual(
compact_ipv4(["10.0.0.0/25", "10.0.1.0/25"]),
["10.0.0.0/25", "10.0.1.0/25"],
)
def test_ip_versions_must_not_be_mixed(self):
with self.assertRaises(TypeError):
list(collapse_addresses([IPv4Network("10.0.0.0/24"), IPv6Network("2001:db8::/64")]))
if __name__ == "__main__":
unittest.main()
Policy ไม่ได้ยุบตาม network เสมอ
ผลลัพธ์รักษา “ชุด address ที่ครอบคลุม” แต่ไม่ได้รักษา metadata ของกฎ เช่น owner, expiry, reason หรือ action ถ้า /25 แรกเป็น allow และอีกครึ่งเป็น deny ห้ามรวมแม้ช่วง address ต่อกัน ต้อง group ตาม semantics ก่อน collapse แล้วตรวจว่า policy เดียวกันจริง
แยก IPv4 กับ IPv6 ก่อน เพราะการปนกันยก TypeError และ parse ด้วย strict=True เมื่อ input ควรเป็น network address จริง หากต้องยอม host bits ให้ระบุ strict=False อย่างตั้งใจและ log normalization
Recap
- รวมเฉพาะ network ที่มี policy/metadata เดียวกัน
- แยก IPv4 และ IPv6 ก่อน collapse
- ใช้ strict parsing ที่ boundary ถ้า host bits คือ error
- เปรียบเทียบ coverage ก่อนและหลังใน test