🧮

CIDR表記を計算するツールをPythonで作ってみた 

2024/12/29に公開

算数苦手な人のために

IP CIDRを計算する方法がさっぱりわからない🤦
計算してくれるサイトはあるようだ。
https://www.softel.co.jp/labs/tools/network/

でも知らなかったので、Pythonで作ってみた。ipaddress --- IPv4/IPv6 操作ライブラリを使用して作成してみました。標準のモジュールとして入っているみたい?
pip installは不要!

https://youtu.be/cEXXV0VGUKQ

example

サンプルコードはこちら。実行するとGUIが表示される。ここに、イチ・クニなんとかって入力します。

main.py
import ipaddress
from typing import List, Dict
import tkinter as tk
from tkinter import ttk, messagebox


class IPCalculator:
    def __init__(self):
        self.window = tk.Tk()
        self.window.title("IPアドレス計算ツール")
        self.window.geometry("600x800")

        # メインフレーム
        main_frame = ttk.Frame(self.window, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # 入力フィールド
        ttk.Label(main_frame, text="IPアドレス:").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.ip_entry = ttk.Entry(main_frame, width=20)
        self.ip_entry.grid(row=0, column=1, sticky=tk.W, pady=5)
        self.ip_entry.insert(0, "192.168.1.0")

        ttk.Label(main_frame, text="CIDR:").grid(row=1, column=0, sticky=tk.W, pady=5)
        self.cidr_combo = ttk.Combobox(main_frame, values=[str(i) for i in range(24, 33)], width=5)
        self.cidr_combo.grid(row=1, column=1, sticky=tk.W, pady=5)
        self.cidr_combo.set("24")

        ttk.Label(main_frame, text="分割数:").grid(row=2, column=0, sticky=tk.W, pady=5)
        self.subnet_combo = ttk.Combobox(main_frame, values=["2", "4", "8", "16"], width=5)
        self.subnet_combo.grid(row=2, column=1, sticky=tk.W, pady=5)
        self.subnet_combo.set("4")

        # 計算ボタン
        calc_button = ttk.Button(main_frame, text="計算する", command=self.calculate)
        calc_button.grid(row=3, column=0, columnspan=2, pady=10)

        # 結果表示エリア
        self.result_text = tk.Text(main_frame, height=20, width=60)
        self.result_text.grid(row=4, column=0, columnspan=2, pady=5)

    def calculate_subnets(self, ip_str: str, current_cidr: int, subnet_count: int) -> List[Dict[str, str]]:
        try:
            # IPネットワークオブジェクトの作成
            network = ipaddress.ip_network(f"{ip_str}/{current_cidr}", strict=False)

            # 必要なビット数を計算
            required_bits = (subnet_count - 1).bit_length()
            new_prefix = current_cidr + required_bits

            if new_prefix > 32:
                raise ValueError("分割数が大きすぎます")

            # サブネットの計算
            subnets = list(network.subnets(prefixlen_diff=required_bits))
            result = []

            for i, subnet in enumerate(subnets[:subnet_count]):
                subnet_info = {
                    "network": str(subnet),
                    "first_host": str(list(subnet.hosts())[0]) if subnet.num_addresses > 2 else "N/A",
                    "last_host": str(list(subnet.hosts())[-1]) if subnet.num_addresses > 2 else "N/A",
                    "broadcast": str(subnet.broadcast_address) if subnet.num_addresses > 1 else "N/A",
                    "num_addresses": subnet.num_addresses,
                    "mask": str(subnet.netmask)
                }
                result.append(subnet_info)

            return result

        except ValueError as e:
            raise ValueError(f"無効な入力: {str(e)}")

    def calculate(self):
        try:
            ip_str = self.ip_entry.get()
            current_cidr = int(self.cidr_combo.get())
            subnet_count = int(self.subnet_combo.get())

            results = self.calculate_subnets(ip_str, current_cidr, subnet_count)

            # 結果の表示
            self.result_text.delete(1.0, tk.END)
            for i, subnet in enumerate(results, 1):
                self.result_text.insert(tk.END, f"\nサブネット {i}:\n")
                self.result_text.insert(tk.END, f"ネットワーク: {subnet['network']}\n")
                self.result_text.insert(tk.END, f"サブネットマスク: {subnet['mask']}\n")
                self.result_text.insert(tk.END, f"使用可能アドレス数: {subnet['num_addresses'] - 2}\n")
                if subnet['first_host'] != "N/A":
                    self.result_text.insert(tk.END, f"最初のホスト: {subnet['first_host']}\n")
                    self.result_text.insert(tk.END, f"最後のホスト: {subnet['last_host']}\n")
                    self.result_text.insert(tk.END, f"ブロードキャスト: {subnet['broadcast']}\n")
                self.result_text.insert(tk.END, "-" * 50 + "\n")

        except ValueError as e:
            messagebox.showerror("エラー", str(e))

    def run(self):
        self.window.mainloop()


if __name__ == "__main__":
    calculator = IPCalculator()
    calculator.run()

こんなGUIが表示されます。

試しにAWS公式のサイトに記載されているIP Adressを4つのサブネットに分けてもらった。

クラス C
クラス C の IPv4 アドレスには 24 のネットワークプレフィックスビットがあります。例えば、192.168.1.100 を考えてみましょう。ここで、192.168.1 はネットワークアドレスで、100 はホストアドレスです。

https://aws.amazon.com/jp/what-is/cidr/

実行結果

サブネット 1:
ネットワーク: 192.168.1.0/26
サブネットマスク: 255.255.255.192
使用可能アドレス数: 62
最初のホスト: 192.168.1.1
最後のホスト: 192.168.1.62
ブロードキャスト: 192.168.1.63
--------------------------------------------------

サブネット 2:
ネットワーク: 192.168.1.64/26
サブネットマスク: 255.255.255.192
使用可能アドレス数: 62
最初のホスト: 192.168.1.65
最後のホスト: 192.168.1.126
ブロードキャスト: 192.168.1.127
--------------------------------------------------

サブネット 3:
ネットワーク: 192.168.1.128/26
サブネットマスク: 255.255.255.192
使用可能アドレス数: 62
最初のホスト: 192.168.1.129
最後のホスト: 192.168.1.190
ブロードキャスト: 192.168.1.191
--------------------------------------------------

サブネット 4:
ネットワーク: 192.168.1.192/26
サブネットマスク: 255.255.255.192
使用可能アドレス数: 62
最初のホスト: 192.168.1.193
最後のホスト: 192.168.1.254
ブロードキャスト: 192.168.1.255
--------------------------------------------------

最後に

計算が苦手な人はツールを使いましょう。そのために作られたのだと思う💦

Discussion