複数のNICがある場合、すべてのIPアドレスを確認するにはどうすればよいですか?
-
06-07-2019 - |
質問
コンピューターに複数のネットワークインターフェイスカードがあり、それぞれに独自のIPアドレスがあります。
Pythonの(組み込み) socket
モジュールから gethostbyname(gethostname())
を使用すると、そのうちの1つだけが返されます。他の人をどうやって手に入れますか?
解決
netifaces
モジュールを使用します。ネットワークは複雑であるため、netifacesを使用するのは少し難しい場合がありますが、ここでは必要なことを行う方法を示します。
>>> import netifaces
>>> netifaces.interfaces()
['lo', 'eth0']
>>> netifaces.ifaddresses('eth0')
{17: [{'broadcast': 'ff:ff:ff:ff:ff:ff', 'addr': '00:11:2f:32:63:45'}], 2: [{'broadcast': '10.0.0.255', 'netmask': '255.255.255.0', 'addr': '10.0.0.2'}], 10: [{'netmask': 'ffff:ffff:ffff:ffff::', 'addr': 'fe80::211:2fff:fe32:6345%eth0'}]}
>>> for interface in netifaces.interfaces():
... print netifaces.ifaddresses(interface)[netifaces.AF_INET]
...
[{'peer': '127.0.0.1', 'netmask': '255.0.0.0', 'addr': '127.0.0.1'}]
[{'broadcast': '10.0.0.255', 'netmask': '255.255.255.0', 'addr': '10.0.0.2'}]
>>> for interface in netifaces.interfaces():
... for link in netifaces.ifaddresses(interface)[netifaces.AF_INET]:
... print link['addr']
...
127.0.0.1
10.0.0.2
これは、次のようにもう少し読みやすくすることができます:
from netifaces import interfaces, ifaddresses, AF_INET
def ip4_addresses():
ip_list = []
for interface in interfaces():
for link in ifaddresses(interface)[AF_INET]:
ip_list.append(link['addr'])
return ip_list
IPv6アドレスが必要な場合は、 AF_INET
ではなく AF_INET6
を使用します。 netifaces
があちこちでリストと辞書を使用する理由を知りたい場合は、単一のコンピューターに複数のNICがあり、各NICに複数のアドレスがあり、各アドレスに独自のオプションセットがあるためです。
他のヒント
import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]
netifaces
モジュールの助けを借りて、すべてのアドレスを1行で:
[netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr'] for iface in netifaces.interfaces() if netifaces.AF_INET in netifaces.ifaddresses(iface)]
完全を期すために、別のオプションは psutil を使用することです。
tldr;
import socket
import psutil
def get_ip_addresses(family):
for interface, snics in psutil.net_if_addrs().items():
for snic in snics:
if snic.family == family:
yield (interface, snic.address)
ipv4s = list(get_ip_addresses(socket.AF_INET))
ipv6s = list(get_ip_addresses(socket.AF_INET6))
説明
必要な関数は net_if_addrs
です。つまり:
import psutil
psutil.net_if_addrs()
次のような結果になります(Python 3):
{'br-ae4880aa80cf': [snic(family=<AddressFamily.AF_INET: 2>, address='172.18.0.1', netmask='255.255.0.0', broadcast='172.18.0.1', ptp=None),
snic(family=<AddressFamily.AF_PACKET: 17>, address='02:42:e5:ae:39:94', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
'docker0': [snic(family=<AddressFamily.AF_INET: 2>, address='172.17.0.1', netmask='255.255.0.0', broadcast='172.17.0.1', ptp=None),
snic(family=<AddressFamily.AF_PACKET: 17>, address='02:42:38:d2:4d:77', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
'eno1': [snic(family=<AddressFamily.AF_PACKET: 17>, address='54:be:f7:0b:cf:a9', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
'lo': [snic(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast=None, ptp=None),
snic(family=<AddressFamily.AF_PACKET: 17>, address='00:00:00:00:00:00', netmask=None, broadcast=None, ptp=None)],
'wlp2s0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.4', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),
snic(family=<AddressFamily.AF_PACKET: 17>, address='00:21:27:ee:d6:03', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}
(Python 2):
{'br-ae4880aa80cf': [snic(family=2, address='172.18.0.1', netmask='255.255.0.0', broadcast='172.18.0.1', ptp=None),
snic(family=17, address='02:42:e5:ae:39:94', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
'docker0': [snic(family=2, address='172.17.0.1', netmask='255.255.0.0', broadcast='172.17.0.1', ptp=None),
snic(family=17, address='02:42:38:d2:4d:77', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
'eno1': [snic(family=17, address='54:be:f7:0b:cf:a9', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
'lo': [snic(family=2, address='127.0.0.1', netmask='255.0.0.0', broadcast=None, ptp=None),
snic(family=17, address='00:00:00:00:00:00', netmask=None, broadcast=None, ptp=None)],
'wlp2s0': [snic(family=2, address='192.168.1.4', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),
snic(family=17, address='00:21:27:ee:d6:03', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}
注:各インターフェイスに同じファミリーの複数のアドレスを関連付けることができるため、dict値はリストです。
各 snic
は です。 namedtuple
には5つのフィールドが含まれます:
-
family
:MAC_アドレスを参照するアドレスファミリ、AF_INET、AF_INET6、またはpsutil.AF_LINKのいずれか。 -
address
:プライマリNICアドレス(常に設定)。 -
netmask
:ネットマスクアドレス(なしの場合もあります)。 -
broadcast
:ブロードキャストアドレス(なしの場合もあります)。 -
ptp
:&#8220;ポイントツーポイント&#8221 ;;ポイントツーポイントインターフェイス(通常はVPN)の宛先アドレスです。 broadcastとptpは相互に排他的です(Noneの場合もあります)。
https://docs.python.org/3.4/library/ socket.html#socket.if_nameindex
socket.if_nameindex()
ネットワークインターフェイス情報(インデックスint、名前文字列)タプルのリストを返します。システムコールが失敗した場合のOSError。
可用性:Unix。
バージョン3.3の新機能。
Python 3.4、UNIX / Linuxで実行可能なこのコードを作成しました
#!/env/python3.4
import socket
import fcntl
import struct
def active_nic_addresses():
"""
Return a list of IPv4 addresses that are active on the computer.
"""
addresses = [ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1]
return addresses
def get_ip_address( NICname ):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', NICname[:15].encode("UTF-8"))
)[20:24])
def nic_info():
"""
Return a list with tuples containing NIC and IPv4
"""
nic = []
for ix in socket.if_nameindex():
name = ix[1]
ip = get_ip_address( name )
nic.append( (name, ip) )
return nic
if __name__ == "__main__":
print( active_nic_addresses() )
print( nic_info() )
次のようなものを印刷します:
['192.168.0.2']
[('lo', '127.0.0.1'), ('enp3s0', '192.168.0.2')]
Linuxのみですが、ここには非常に簡単なレシピがあります http://code.activestate.com / recipes / 439094 /
おそらく、別の回答に記載されている netifacesパッケージと同様のコードを使用します(ただし、ここにリンクされているバージョン)
socket.getaddrinfo()は、実際にはデバイスのバインドされたIPアドレスを返しません。 hostsファイルに&quot; 127.0.1.1 yourhost.example.com yourhost&quot;という行が含まれている場合(これは一般的な構成です)、getaddrinfoは127.0.1.1のみを返します。
これは、すべてのIPv4およびIPv6インターフェースを見つけるためのルーチンです。前のポスターが指摘したように、socket.gethostbyname_ex()はIPv6で機能しません。Pythonのドキュメントでは、 socket.getaddressinfo()代わりに。
このルーチンは、コールバックIPv4インターフェース(127.0.0.1)を追加し、IPv6インターフェースがある場合は、コールバックIPv6インターフェース(:: 1)も追加します。私のマシンでは、socket.getaddrinfo()はこれらのいずれかまたは両方を提供しますが、使用可能な他のインターフェースがない場合のみです。
必要に応じて、使用可能な各インターフェイスの指定されたポートでUDPソケットを開こうとしたので、コードに「ポート」があります。そして、socket.SOCK_DGRAMが含まれます。それらを変更しても安全です。ポートを念頭に置いていない場合。
addrinfo_ipv4 = socket.getaddrinfo(hostname,port,socket.AF_INET,socket.SOCK_DGRAM)
addrinfo_ipv6 = []
try:
addrinfo_ipv6 = socket.getaddrinfo(hostname,port,socket.AF_INET6,socket.SOCK_DGRAM)
except socket.gaierror:
pass
addrinfo = [(f,t,a) for f,t,p,cn,a in addrinfo_ipv4+addrinfo_ipv6]
addrinfo_local = [(socket.AF_INET,socket.SOCK_DGRAM,('127.0.0.1',port))]
if addrinfo_ipv6:
addrinfo_local.append( (socket.AF_INET6,socket.SOCK_DGRAM,('::1',port)) )
[addrinfo.append(ai) for ai in addrinfo_local if ai not in addrinfo]
このスニペットは、システムで使用可能なすべてのIPV4アドレスのリストを提供します。
import itertools
from netifaces import interfaces, ifaddresses, AF_INET
links = filter(None, (ifaddresses(x).get(AF_INET) for x in interfaces()))
links = itertools.chain(*links)
ip_addresses = [x['addr'] for x in links]
すべてのIP構成済みIPアドレスを直接取得する必要があります。 ifconfigを実行し、その出力を解析します( ifconfigがPythonで直接行うことも可能です、 Cでの実行方法を確認)。ホスト名が必要な場合は、gethostbyaddrを使用します。
次のように簡単に実行できます。
import netifaces
for interface in netifaces.interfaces():
print netifaces.ifaddresses(interface)
詳細については、 netifacesのドキュメントをご覧ください。
@Harley Holcombeの答えは有効だと思いますが、IPのない仮想NICがある場合はエラーになります。だからこれは私が修正されました:
def get_lan_ip():
for interface in interfaces():
try:
for link in ifaddresses(interface)[AF_INET]:
if str(link['addr']).startswith("172."):
return str(link['addr'])
except:
pass
これは、lan ipv4のみを返します
このスレッドが示すように、同じ結果を達成する多くの方法があります。私の提案する方法は、 getaddrinfo()
の組み込みファミリフィルターを活用し、次のように標準化タプルを解析することです。 :
from socket import getaddrinfo, AF_INET, gethostname
for ip in getaddrinfo(host=gethostname(), port=None, family=AF_INET):
print(ip[4][0])
出力例:
192.168.55.1
192.168.170.234