サブネット内の機器検索
Python上でnmapを用い、サブネット内の機器を検索する。複数ネットワークI/Fにも対応。
nmap
オプション「-sn」(ARP)により、同一サブネット内の機器を検索する。コマンド的には下記となる。
- nmap -sn 192.168.1.0/24
パケット的には、ARPブロードキャストを下限から上限まで実施している。
Pythonでのnmap
下記サイトを参照。
ソースコード
サブネットマスクをビット数に変換
ネットワークI/Fごとに得られるサブネット情報(後述)が「255.255.255.0」となっており、nmapコマンドに引数として渡す時に、「/24」のように指定する必要がある。サイト「PythonでCIDR表記のネットマスクをドット付き十進表記に変換」をそのまま利用した。
import socket
import struct
def mask2cidr(mask):
return bin(struct.unpack('!L', socket.inet_pton(socket.AF_INET, mask))[0])[2:].index('0')
例えば、「255.255.255.0」を「24」に変換する。
ネットワークI/F名の取得
import netifaces
for iface in netifaces.interfaces():
「iface」として、Windowsでは「{362E1251-D728-413E-897B-0A4BB1DCAB97}」のようなアウトプットとなる。Linuxなどでは「eth0」となる。
ネットワークI/F情報の取得
conf = netifaces.ifaddresses(iface)
例えば、次のような結果が得られる。
{-1000: [{'addr': '4c:bb:58:rr:kk:82'}],
2: [{'addr': '192.168.10.109', 'netmask': '255.255.255.0', 'broadcast': '192.168.10.255'}]}
Key'2'にI/FのIPアドレスなどが格納されている。
nmapを実行
if 2 in conf:
adr = str(conf[2][0]['addr'])
if adr == '127.0.0.1' or '169.254' in adr:
continue
search = adr + '/' + str(mask2cidr(conf[2][0]['netmask']))
print(search)
nm = nmap.PortScanner()
output = nm.scan(hosts=search, arguments='-sn')
dic = output['scan']
ループバックアドレスやリンクアドレスが付与されているI/Fを除いた全I/Fで、nmapによる機器検索(-snオプション)を実施する。「scan」の結果(上記の変数'dic')例は下記となる。
'192.168.10.1': {'hostnames': [{'name': 'aterm.me', 'type': 'PTR'}], 'addresses': {'ipv4': '192.168.10.1', 'mac': '80:22:A7:ZZ:SS:52'}, 'vendor': {'80:22:A7:ZZ:SS:52': 'NEC Platforms'}, 'status': {'state': 'up', 'reason': 'arp-response'}},
'192.168.10.109': {'hostnames': [{'name': '', 'type': ''}], 'addresses': {'ipv4': '192.168.10.109'}, 'vendor': {}, 'status': {'state': 'up', 'reason': 'localhost-response'}}}
IPアドレスとMacアドレスの取得
for key in dic:
ip = dic[key]['addresses']['ipv4']
if 'mac' in dic[key]['addresses']:
m = dic[key]['addresses']['mac']
else:
m = ''
print(' IP address: %s\tMac address: %s' % (ip, m))
「scan」の結果からそれぞれを抽出。
コード全体
import netifaces
import nmap
import socket
import struct
def mask2cidr(mask):
"""Convert netmask from Dotted address to CIDR."""
return bin(struct.unpack('!L', socket.inet_pton(socket.AF_INET, mask))[0])[2:].index('0')
for iface in netifaces.interfaces():
conf = netifaces.ifaddresses(iface)
if 2 in conf:
adr = str(conf[2][0]['addr'])
if adr == '127.0.0.1' or '169.254' in adr:
continue
search = adr + '/' + str(mask2cidr(conf[2][0]['netmask']))
print(search)
nm = nmap.PortScanner()
output = nm.scan(hosts=search, arguments='-sn')
dic = output['scan']
for key in dic:
ip = dic[key]['addresses']['ipv4']
if 'mac' in dic[key]['addresses']:
m = dic[key]['addresses']['mac']
else:
m = ''
print(' IP address: %s\tMac address: %s' % (ip, m))
結果
192.168.2.16/24
IP address: 192.168.2.1 Mac address: AE:87:A3:xx:yy:64
IP address: 192.168.2.16 Mac address:
192.168.56.1/24
IP address: 192.168.56.1 Mac address:
192.168.219.1/24
IP address: 192.168.219.1 Mac address:
192.168.137.1/24
IP address: 192.168.137.1 Mac address:
192.168.42.1/24
IP address: 192.168.42.254 Mac address: 00:50:56:zz:bb:EE
IP address: 192.168.42.1 Mac address:
192.168.59.1/24
IP address: 192.168.59.254 Mac address: 00:50:56:rr:ww:0E
IP address: 192.168.59.1 Mac address:
192.168.10.109/24
IP address: 192.168.10.1 Mac address: 80:22:A7:qq:pp:52
IP address: 192.168.10.105 Mac address: 6C:40:08:kk:vv:44
IP address: 192.168.10.109 Mac address:
このPythonのモジュールでは、自身のI/FのMac Addressは取得されないようだ。また、他の機器のMac Addressは取得されないケースもあった。このあたりの詳細は未調査。
EOF