【マスクとアドレスと論理式】pythonでのbit演算でIPv4を計算する
何コレ?
#! /usr/bin/env python # -*- coding: utf-8 -*- import sys import socket import struct import re type1 = re.compile(ur'([\d\.]+)\/(\d+)') # 192.168.0.1/24 type2 = re.compile(ur'([\d\.]+)\s+([\d\.]+)') # 192.168.0.1/ 255.255.255.0 acl_serach_max = 8192 acl_search_ip_max = 4 def aton(ipstr): return struct.unpack(r'!l', socket.inet_aton(ipstr))[0] def ntoa(ip): return socket.inet_ntoa(struct.pack(r'!l', ip)) def net_addr(ip_addr, subnet): return (ip_addr & subnet) def bcast_addr(ip_addr, subnet): return ip_addr | ~subnet def bit_mask(bit): if 32 <= bit: bin = r'0' elif bit <= 0: bin = r'1' * 32 else: bin = r'1' * (32 - bit) return ~int(bin, 2) def check_subnet(ip_addr, subnet, ip): if ((ip & subnet) ^ (ip_addr & subnet)) == 0: return True else: return False def check_wildcard(ip_addr, wildcard, ip): if ((ip & ~wildcard) ^ (ip_addr & ~wildcard)) == 0: return True else: return False def count_wildcard(ip_start, ip_end, wildcard): if not bin(wildcard).lstrip(r'0b').find(r'0') == -1: i = 0 ip_addrs = [] for ip in xrange(ip_start, ip_end+1): if check_wildcard(ip_start, wildcard, ip): ip_addrs.append(ntoa(ip)) i += 1 if i > acl_serach_max or len(ip_addrs) >= acl_search_ip_max: ip_addrs.append(r"...") break return ip_addrs else: return (ip_end - ip_start) + 1 def parse(ip, mask): if 0 < mask: # net_addr wildcard_start = ip & ~mask # bcast_addr wildcard_end = ip | mask print r'ACL', print ntoa(ip), r'->', ntoa(wildcard_start), r'-', ntoa(wildcard_end), count_wildcard(wildcard_start, wildcard_end, mask) else: if check_subnet(aton(r'10.0.0.0'), aton(r'255.0.0.0'), ip): print r'PrivateA', elif check_subnet(aton(r'172.16.0.0'), aton(r'255.240.0.0'), ip): print r'PrivateB', elif check_subnet(aton(r'192.168.0.0'), aton(r'255.255.0.0'), ip): print r'PrivateC', elif check_subnet(aton(r'169.254.0.0'), aton(r'255.255.0.0'), ip): print r'APIPA', elif check_subnet(aton(r'224.0.0.0'), aton(r'240.0.0.0'), ip): print r'Multicast', else: print r'Global', net = net_addr(ip, mask) bcast = bcast_addr(ip, mask) print ntoa(ip), r'->', ntoa(net), r'/', ntoa(mask), ntoa(bcast), (bcast - net) + 1 def entry(): print r'[[ IP Range 1.0.0 ]]' for line in iter(sys.stdin.readline, r''): try: line = line.strip() rr = type1.findall(line) for sIp, sMask in rr: ip = aton(sIp) mask = bit_mask(int(sMask)) parse(ip, mask) line = type1.sub(r'', line) rr = type2.findall(line) for sIp, sMask in rr: ip = aton(sIp) mask = aton(sMask) parse(ip, mask) except socket.error, e: print 'Syntax Error:', e except (ee, e): print 'Error:', ee, e if __name__ == '__main__': reload(sys) sys.setdefaultencoding(r'utf-8') entry()
簡単に言えばIPアドレスのレンジを計算してくれる計算機だ。
もちろんpythonで書いた。
いやー、持ち込み何でも可っていうのはイイネ!
どんな計算機?
ネットワークアドレス、サブネットマスク、ブロードキャスト、IPの数がスパッとわかる。
[[ IP Range 1.0.0 ]] 192.168.10.245/24[エンター] PrivateC 192.168.10.245 -> 192.168.10.0 / 255.255.255.0 192.168.10.255 256 172.16.32.254/12[エンター] PrivateB 172.16.32.254 -> 172.16.0.0 / 255.240.0.0 172.31.255.255 1048576
もちろん複数行や表記ゆれ、CIDRにも対応
10.0.0.10/16 192.168.7.5 255.255.255.248 172.23.12.5/24[エンター] PrivateA 10.0.0.10 -> 10.0.0.0 / 255.255.0.0 10.0.255.255 65536 PrivateB 172.23.12.5 -> 172.23.12.0 / 255.255.255.0 172.23.12.255 256 PrivateC 192.168.7.5 -> 192.168.7.0 / 255.255.255.248 192.168.7.7 8
しかもACLのワイルドカードマスクまで計算してくれるスグレもの
192.168.0.0 0.0.0.255 192.168.32.14 0.0.255.255[エンター] ACL 192.168.0.0 -> 192.168.0.0 - 192.168.0.255 256 ACL 192.168.32.14 -> 192.168.0.0 - 192.168.255.255 65536
マスクとアドレスと論理式
そもそもアドレスだのマスクだのパソコンの都合がいい値を無理に表示して192.168.1.1だの255.255.255.0だのしてる訳であってIPv6の省略記法なんてすぐには覚えれそうに無イネ!
それでもアドレスとサブネットマスクからネットワークアドレスを求めてみる
まずは2進数で計算だ。そんな簡単な事を言うがHaskhellとかSchemeをやってそうな関数型言語を扱う高等民族とかとは別の至高なプログラマ、バイナリアンにしか直ぐには計算できない芸当であろう。
10101100.00010000.00011100.11101011 # 172.16.28.235(IPアドレス) AND 11111111.11111111.11111111.11111000 # 255.255.255.248(サブネット) --------------------------------------- 10101100.00010000.00011100.11101000 # 172.16.28.232(ネットワーク) network_address = ip_address & subnetmask # プログラムで表すと…
ANDで計算すればサクっとできる!そんな簡単な事を言うが(略)
ブロードキャストは?
10101100.00010000.00011100.11101011 # 172.16.28.235(IPアドレス) OR 00000000.00000000.00000000.00000111 # 0.0.0.7(サブネットのインバース) --------------------------------------- 10101100.00010000.00011100.11101111 # 172.16.28.239(ブロードキャスト) broadcast_address = ip_address | ~subnetmask # プログラムで表すと…
サブネットマスクをNOTでインバース(0と1を反転したもの)とし、IPアドレスとORしたものがブロードキャストだ。
しかし馬鹿な俺は最初にプログラムで書いたのが…
network_address = ip_address & subnetmask broadcast_address = network_address + ~subnetmask
まだ俺は至高民族バイナリアンにはなれそうにないです。
式と範囲とワイルドカード
ワイルドカードマスクってなんぞや?簡単に言わせればワイルドカードマスクはサブネットマスクのインバースだ。(嘘)よってワイルドカードマスクの範囲はワイルドカードマスクを反転したものをサブネットとしてネットワークアドレスから始まりでブロードキャストアドレスで終わる簡単な物だ。(嘘)
172.16.28.235 0.0.0.7[エンター] ACL 172.16.28.235 -> 172.16.28.232 - 172.16.28.239 8 172.16.28.235 0.0.3.255[エンター] ACL 172.16.28.235 -> 172.16.28.0 - 172.16.31.255 1024
だがちょっと待って欲しい。ワイルドカードをもっと知るためにはサブネットマスクについてもっと知っておく必要がある。
10000000.00000000.00000000.00000000 # /1 ... 11111111.00000000.00000000.00000000 # /8 ... 11111111.11111111.11111111.00000000 # /24 ... 11111111.11111111.11111111.11111000 # /29 255.254.255.248 左から右に1が増えていく…
サブネットマスクの最小クラスは/1である。そこから左から右に向かって埋まっていく。よって32ビット符号付き整数で表すとサブネットマスクは必ずマイナス値である。その符号の部分がサブネットマスクで表した時の128.000.000.000の128の部分になる。ここで重要な事が一つある。上の表を見て気づいたかもしれないが、サブネットマスクは1で埋まったところに0が紛れることは絶対に起きない。
11111111.11111111.11111111.11111 0 10 # 255.255.255.250 このような値にもならない。 ↑ 11111111.1111111 0 .11111111.11111000 ↑絶対に0が紛れるようなことは起きない。
サブネットマスクでは以上のようなケースは絶対に起きないということだ。しかしワイルドカードマスクは違う、もう入らないなんてもんじゃない。超入る。とにかくワイルドカードマスクのヤバさをもっと知るべきだと思います。(この部分を書きたいためにこの記事書きました。)
ワイのワイルドカードなワイバーンや!
ということは以下のようなワイルドカードマスクも設定できるのである。
00000000.00000000.00000001.00001000 # 0.0.1.4 ↑ 1と1の間に0が挟まれている
以上のワイルドカードマスクを見て、もし(192.168.1.0 0.0.1.4)とかいう設定をACLでみしまったときアナタはどうするだろう。192.168.0.0〜192.168.1.4とかいうアナタは痛い目にみるかもしれない。正解は(192.168.0.0, 192.168.0.4, 192.168.1.0, 192.168.1.4)の4つだけである。
ACLのやっている処理を探る
11000000.10101000.00000001.00000000 # 192.168.1.0(IPアドレス) AND 11111111.11111111.11111110.11110111 # 0.0.1.4(ワイルドカードのインバース) --------------------------------------- 11000000.10101000.00000000.00000000 # 192.168.0.0
まずACLの範囲はACLに設定されたIPアドレスにワイルドカードのインバースにANDを施したアドレスを求める。(実質、指定したIPアドレスのネットワークアドレスを求めたモノと同じ)
11000000.10101000.00000001.00000011 # 192.168.1.3(192.168.0.0〜192.168.1.4の間にありそうなIP) AND 11111111.11111111.11111110.11110111 # 0.0.1.4(ワイルドカードのインバース) --------------------------------------- 11000000.10101000.00000000.00000011 # 192.168.0.3
次にチェックされるIPアドレスに同じようにワイルドカードのインバースをANDで施したアドレスを求め、上で求めたアドレスと比較し合致するか合致しないかを判定するのだ。(実質的に、上のアドレスとネットワークアドレスが違ってしまった!これでは別ネットワークと判断される)
わかりやすく…?
最初の処理でしているのは… 11000000.10101000.00000001.00000000 # 192.168.1.0(IPアドレス) 00000000.00000000.00000001.00001000 # 0.0.1.4(ワイルドカード) 11000000.10101000.0000000*.0000*000 # *の部分だけ無視することができる そしてチェックするIPと比べてみると… 11000000.10101000.00000001.00000011 # 192.168.1.3 11000000.10101000.0000000*.0000*000 # ↑*以外の部分が違っている!!
2つのビットの比較はコンピュータではXORでサクッと調べることが出来る。0が帰ってきたら正解だ。
((ip & subnet) ^ (ip_addr & subnet)) # サブネットマスクによる同一ネットワークのチェック ((ip & ~wildcard) ^ (ip_addr & ~wildcard)) # ワイルドカードによるチェック
偶数だけ、奇数だけのACLの種明かし
そんな狂気じみたACLを設定して視認性が〜とかなる前にこれを応用したのが奇数だけ偶数だけのACLの設定方法である。
11000000.10101000.00000001.00000000 # 192.168.1.0(IPアドレス、最後が偶数でビットは0) 00000000.00000000.00000000.11111110 # 0.0.0.254(ワイルドカード) 11000000.10101000.00000001.*******0 # チェックするIPの最後のビットが0の時だけ判定される ↑*の部分の判定は行われない。
最後のビットが0かどうかが判断され、また最後のビットが0の値は偶数だ。
11000000.10101000.00000001.00000001 # 192.168.1.1(IPアドレス、最後が偶数でビットは0) 00000000.00000000.00000000.11111110 # 0.0.0.254(ワイルドカード) 11000000.10101000.00000001.*******1 # チェックするIPの最後のビットが1の時だけ判定される
また最後のビットが1の値はなのは偶数だけである。以上でワイルドカードはサブネットマスクのインバースでなくフラグとしてのマスクであることを理解してくれれば嬉しい。
最後に
この記事は現在CCNAも取ってない状態で記述されました(´・ω・`)