web-dev-qa-db-ja.com

複数のNICがある場合、すべてのIPアドレスをどのように決定しますか?

コンピューターに複数のネットワークインターフェイスカードがあり、それぞれに独自のIPアドレスがあります。

Pythonの(組み込み)socketモジュールからgethostbyname(gethostname())を使用すると、そのうちの1つだけが返されます。他のユーザーを取得するにはどうすればよいですか?

38
Wilson F

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_INET6 の代わりに AF_INETnetifacesがリストや辞書をあちこちで使用している理由がわからない場合は、1台のコンピュータに複数のNICを使用でき、各NICに複数のアドレスを使用でき、それぞれにアドレスには独自のオプションセットがあります。

50
Harley Holcombe
import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]
15
Nakilon

完全を期すために、別のオプションは 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値はリストです。

snicnamedtuple で、5つのフィールドが含まれます。

  • family:MACアドレスを参照する、AF_INET、AF_INET6、またはpsutil.AF_LINKのいずれかのアドレスファミリ。
  • address:プライマリNICアドレス(常に設定されています)。
  • netmask:ネットマスクアドレス(Noneの場合があります)。
  • broadcast:ブロードキャストアドレス(Noneの場合があります)。
  • ptp:「ポイントツーポイント」の略です。ポイントツーポイントインターフェイス(通常はVPN)の宛先アドレスです。 broadcastとptpは相互に排他的です(Noneの場合があります)。
8
pmav99

netifacesモジュールの助けを借りて、1行にすべてのアドレス:

[netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr'] for iface in netifaces.interfaces() if netifaces.AF_INET in netifaces.ifaddresses(iface)]
8
Elemag

https://docs.python.org/3.4/library/socket.html#socket.if_nameindex

socket.if_nameindex()

ネットワークインターフェイス情報(インデックス整数、名前文字列)タプルのリストを返します。システムコールが失敗した場合は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')]
3
The Demz

このスレッドが示すように、同じ結果を得るには多くの方法があります。推奨される方法は、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
1
chjortlund

以下は、すべてのIPv4およびIPv6インターフェースを見つけるためのルーチンです。以前の投稿者が指摘したように、socket.gethostbyname_ex()はIPv6では機能せず、Pythonドキュメントでは、代わりに socket.getaddressinfo() を使用することを推奨しています。

このルーチンは、コールバックIPv4インターフェース(127.0.0.1)を追加し、IPv6インターフェースがある場合は、コールバックIPv6インターフェース(:: 1)も追加します。私のマシンでは、socket.getaddrinfo()がこれらの1つまたは両方を提供しますが、他に使用可能なインターフェースがない場合のみです。

私のニーズのために、使用可能な各インターフェイスの指定されたポートでUDPソケットを開こうとしたので、コードに "port"と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]
1
DamonJW

これはLinuxのみですが、ここには非常に簡単なレシピがあります http://code.activestate.com/recipes/439094/

おそらく、別の回答で言及されている netifacesパッケージ と同様のコードを使用しています(ただし、現在のバージョンはここにリンクされています)

Socket.getaddrinfo()は実際にはデバイスのバインドされたIPアドレスを返しません。 hostsファイルに「127.0.1.1 yourhost.example.com yourhost」という行が含まれている場合、これは一般的な構成であり、getaddrinfoは127.0.1.1のみを返します。

1
JimB

このスニペットは、システムで使用可能なすべての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]
1
Sandeep

あなたはこのようにしてかなり簡単にそれを行うことができます:

import netifaces

for interface in netifaces.interfaces():
    print netifaces.ifaddresses(interface)

詳細については、 netifacesのドキュメント を参照してください。

0
Tlili Marwen

@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のみを返します

0
yongdi

すべてのIP構成済みIPアドレスを直接取得する必要があります。 ifconfigを実行してその出力を解析する(Pythonで直接 ifconfigが実行することも可能ですCでどのように実行されるかを確認してください )。ホスト名が必要な場合は、gethostbyaddrを使用します。

0