むろっちのStacking

日々の中で学んだIT知識をメモして置く場所

PythonでIPアドレスを簡単に扱う方法

IPアドレスってプログラムから扱おうとするとめんどくさいですよね
数字だけならいいのですが、たいてい192.168.0.2とか間にドットが入ってくる
またサブネットもオクテット刻みであればドットごとに分けて数字として計算すれば加算、減算できますが/23とか 半端な値になると結構めんどくさいです。

2点間のIPレンジの一覧が欲しい時、以下のように書いていました

# 2IPアドレス間の一覧を列挙する
# start_ip '192.168.0.' end_ip '192.168.2.5' subnet '255.255.254.0'
def generate_ipaddr_list(start_ip,end_ip,subnet):
    start_ip_list = [int(i) for i in start_ip.split('.')]
    end_ip_list = [int(i) for i in end_ip.split('.')]
    subnet_list = [int(i) for i in subnet.split('.')]
    int_mask = ip_to_binip(subnet).count('1')
    result_ips = []
    try:
        for oct1_ip in range(start_ip_list[0],end_ip_list[0]+1):
            for oct2_ip in range(start_ip_list[1],end_ip_list[1]+1):
                for oct3_ip in range(start_ip_list[2],end_ip_list[2]+1):
                    for oct4_ip in range(start_ip_list[3],end_ip_list[3]+1):
                        ip_l = [oct1_ip,oct2_ip,oct3_ip,oct4_ip]
                        ip_str = '.'.join([str(i )for i in ip_l])
                        if ip_l <= end_ip_list and ip_to_binip(ip_str)[:int_mask] == ip_to_binip(start_ip)[:int_mask]:
                            result_ips.append(ip_str)
                        else:
                            raise Exception
    except Exception:
        pass
    finally:
        return result_ips

# 10進数のIPをバイナリ形式に変換
def ip_to_binip(str_ip):
    str_ip_list = [int(i) for i in str_ip.split('.')]
    b_str_ip = ''
    for s in str_ip_list:
        b_str_ip += format(s,'b').zfill(8)
    return b_str_ip

一度バイナリ形式に変換してから計算する仕組みです。 Pythonでバイナリ型はIP計算で使いたいような算術演算がないので使いにくいです

計算しにくいなーと思っていたら、そもそもPythonにipaddressってモジュールがありました

docs.python.jp

使い方

import ipaddress as ip
start_ip = ip.IPv4Address('192.168.0.100')
end_ip = ip.IPv4Address('192.168.3.100')

# 特定のIPアドレス間の一覧を表示
i = 0
while start_ip + i <= end_ip:
    now_ip = start_ip + i
    print(now_ip.exploded)
    i += 1 # =< 192.168.0.100 ,192.168.0.101 ... ,192.168.3.100 

# ネットワークセグメントのIP一覧
for addr in ip.IPv4Network('192.168.0.0/23'): # 255.255.254.0でも可
    print(addr.exploded)
# IPアドレスがプライベートか否か
start_ip.is_private
# =>True

# 2進数へ変換
start_ip.packed
# => True

test_ip = ip.IPv4Interface('192.168.2.100')

# IPv4Networkオブジェクトを生成 IPを指定していないと/32
test_ip.network
# => IPv4Network('192.168.2.100/32')

# /XXの形
test_ip.with_prefixlen
# => '192.168.2.100/32'

# /255.255 ~の形
test_ip.with_netmask
# => '192.168.2.100/255.255.255.255'

IPv4InterfaceクラスはIPv4Addressクラスのサブクラスのため、特に問題なければIPv4Interfaceクラスでオブジェクト生成したほうがいいかもしれません