IPアドレスがサブネットに含まれるかどうかを判定する

Ruby版

# coding: utf-8

def ip_to_b(addr)
  addr.split(".").map{|x| sprintf("%08d", x.to_i.to_s(2).to_i)}.join("")
end

def in_subnet?(addr, subnet)
  addr_b = ip_to_b(addr)
  subnet_addr, mask = subnet.split("/")
  subnet_addr_b = ip_to_b(subnet_addr)
  mask = mask.to_i
  addr_b.slice(0...mask) == subnet_addr_b.slice(0...mask)
end

addr = "192.168.1.5"
subnet = "192.168.1.0/24"

puts in_subnet?(addr, subnet)

addr2 = "127.0.0.1"

puts in_subnet?(addr2, subnet)
takatoh@nightschool $ ruby is_in_subnet.rb
true
false

ちょっとトリッキーなのは、ip_to_b メソッドの sprintf のところ。頭に 0 をつけて8桁にするために、2進数(の文字列)にしたものを1と0だけからなる10進数だとみなして、%08d というフォーマットに変換している。これで IPアドレスの . で区切られた4つの部分のそれぞれが8桁(言い換えると8ビット)になる。
結果は上に示したとおり。192.18.1.5 はサブネット 192.168.1.0/24 に含まれるけど、127.0.0.1 は含まれない。

Python版

やってることは Ruby 版と同じ。

# coding: utf-8

def ip_to_b(addr):
    return "".join(map(lambda x: "%08d" % int(bin(int(x))[2:]), addr.split(".")))

def is_in_subnet(addr, subnet):
    addr_b = ip_to_b(addr)
    subnet_addr, mask = subnet.split("/")
    subnet_addr_b = ip_to_b(subnet_addr)
    mask = int(mask)
    return addr_b[:mask] == subnet_addr_b[:mask]

addr = "192.168.1.5"
subnet = "192.168.1.0/25"

print is_in_subnet(addr, subnet)

addr2 = "127.0.0.1"

print is_in_subnet(addr2, subnet)

当然結果も同じ。

takatoh@nightschool $ python is_in_subnet.py
True
False