Linux 仅使用 proc 获取本地网络接口地址?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5281341/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 03:13:36  来源:igfitidea点击:

Get local network interface addresses using only proc?

pythonlinuxnetworkingipv4procfs

提问by Matt Joiner

How can I obtain the (IPv4) addresses for all network interfaces using only proc?After some extensive investigation I've discovered the following:

如何仅使用proc获取所有网络接口的(IPv4)地址?经过一些广泛的调查,我发现了以下内容:

  1. ifconfigmakes use of SIOCGIFADDR, which requires open sockets and advance knowledge of all the interface names. It also isn't documented in any manual pages on Linux.
  2. proccontains /proc/net/dev, but this is a list of interface statistics.
  3. proccontains /proc/net/if_inet6, which is exactly what I need but for IPv6.
  4. Generally interfaces are easy to find in proc, but actual addresses are very rarely used except where explicitly part of some connection.
  5. There's a system call called getifaddrs, which is very much a "magical" function you'd expect to see in Windows. It's also implemented on BSD. However it's not very text-oriented, which makes it difficult to use from non-C languages.
  1. ifconfig使用SIOCGIFADDR,这需要打开套接字并提前了解所有接口名称。它也没有记录在 Linux 的任何手册页中。
  2. proccontains /proc/net/dev,但这是一个接口统计信息列表。
  3. proccontains /proc/net/if_inet6,这正是我需要的,但对于 IPv6。
  4. 通常接口很容易在 中找到proc,但实际地址很少使用,除非是某些连接的明确部分。
  5. 有一个名为 的系统调用getifaddrs,这是您希望在 Windows 中看到的非常“神奇”的函数。它也在 BSD 上实现。然而,它不是非常面向文本的,这使得它很难从非 C 语言中使用。

采纳答案by adobriyan

There is no IPv4 analog of /proc/net/if_inet6

没有 /proc/net/if_inet6 的 IPv4 模拟

ifconfig does:

ifconfig 执行以下操作:

fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)
ioctl(fd, SIOCGIFCONF, ...)

You'll get something like this:

你会得到这样的东西:

ioctl(4, SIOCGIFCONF, {120, {{"lo", {AF_INET, inet_addr("127.0.0.1")}}, {"eth0", {AF_INET, inet_addr("10.6.23.69")}}, {"tun0", {AF_INET, inet_addr("10.253.10.151")}}}})

回答by sarnold

You may find the output of ip addr showeasier to parse than output from other tools:

您可能会发现ip addr show比其他工具的输出更容易解析的输出:

$ ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:24:1d:ce:47:05 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.121/24 brd 192.168.0.255 scope global eth0
    inet6 fe80::224:1dff:fece:4705/64 scope link
       valid_lft forever preferred_lft forever
3: eth1: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN qlen 1000
    link/ether 00:24:1d:ce:35:d5 brd ff:ff:ff:ff:ff:ff
4: virbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN
    link/ether 92:e3:6c:08:1f:af brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
    inet6 fe80::90e3:6cff:fe08:1faf/64 scope link
       valid_lft forever preferred_lft forever

Another option is the file /proc/net/tcp. It shows all currently-open TCP sessions, which is different than what you asked for, but might be Good Enough.

另一种选择是文件/proc/net/tcp. 它显示所有当前打开的 TCP 会话,这与您要求的不同,但可能足够好。

$ cat tcp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode
   0: 00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 13536 1 ffff88019f0a1380 300 0 0 2 -1
   1: 00000000:1355 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 19877854 1 ffff880016e69380 300 0 0 2 -1
   2: 017AA8C0:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 13633 1 ffff88019f0a1a00 300 0 0 2 -1
   3: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 8971 1 ffff88019f0a0000 300 0 0 2 -1
   4: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 12952880 1 ffff880030e30680 300 0 0 2 -1
   5: 00000000:0539 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 14332 1 ffff88019f0a2080 300 0 0 2 -1
   6: 00000000:C000 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 14334 1 ffff88019f0a2700 300 0 0 2 -1
   7: 0100007F:0A44 00000000:0000 0A 00000000:00000000 00:00000000 00000000   119        0 51794804 1 ffff880016e6a700 300 0 0 2 -1
   8: 7900A8C0:B094 53D50E48:01BB 01 00000000:00000000 00:00000000 00000000  1000        0 64877487 1 ffff880100502080 23 4 16 4 -1
   9: 7900A8C0:9576 537F7D4A:01BB 06 00000000:00000000 03:00000E5D 00000000     0        0 0 3 ffff880100c84600
  10: 7900A8C0:CC84 0CC181AE:01BB 01 00000000:00000000 00:00000000 00000000  1000        0 61775908 1 ffff880198715480 35 4 11 4 -1
$ irb
irb(main):001:0> [0x79, 0x00, 0xa8, 0xc0]
=> [121, 0, 168, 192]

My IP is 192.168.0.121; note the funny arithmetic to make it come out right. :)

我的 IP 是192.168.0.121; 注意有趣的算术以使其正确。:)

回答by Foon

It's bass-ackwards and I probably am forgetting a corner case, but if you look at /proc/1/net/route, that has your routing table. If you select lines for which the gateway is 0.0.0.0, the first column is the interface and the second column is the hex representation of your IP address, in network byte order (and the third column is the gateway ip you want to filter on).

这是bass-ackwards,我可能忘记了一个角落案例,但是如果您查看/proc/1/net/route,那有您的路由表。如果您选择网关为 0.0.0.0 的行,第一列是接口,第二列是您的 IP 地址的十六进制表示,以网络字节顺序(第三列是您要过滤的网关 IP) )。

回答by klaus thorn

cat /proc/net/tcp

猫 /proc/net/tcp

Get the second column, with the heading "local_address", e.g. "CF00A8C0:0203"

获取第二列,标题为“local_address”,例如“CF00A8C0:0203”

The part after ":" is a port number.

“:”后面的部分是端口号。

From the rest use the last two (C0) as a hex number, e.g. C0 is 192, which is the start of the address in this example.

其余的使用最后两个 (C0) 作为十六进制数,例如 C0 是 192,这是本示例中地址的开始。

Took the following into my notes a while ago, from some smart point in the net:

不久前,从网络上的一些聪明点将以下内容记入我的笔记:

The IP address is displayed as a little-endian four-byte hexadecimal number; that is, the least significant byte is listed first, so you'll need to reverse the order of the bytes to convert it to an IP address.

IP 地址显示为小端四字节十六进制数;也就是说,首先列出最低有效字节,因此您需要颠倒字节顺序以将其转换为 IP 地址。

The port number is a simple two-byte hexadecimal number.

端口号是一个简单的两字节十六进制数。

回答by F. Hauri

My solution to retrieve IPv4 network config, using /proconly:

/proc仅使用以下方法检索 IPv4 网络配置的解决方案:

Unfortunately, this is bash(bash onlyand withoutany fork), not python. But I hope this will be readable:

不幸的是,这是庆典庆典只没有任何叉),而不是蟒蛇。但我希望这将是可读的:

#!/bin/bash

# ip functions that set variables instead of returning to STDOUT

hexToInt() {
    printf -v  "%d\n" 0x${2:6:2}${2:4:2}${2:2:2}${2:0:2}
}
intToIp() {
    local var= iIp
    shift
    for iIp ;do 
        printf -v $var "%s %s.%s.%s.%s" "${!var}" $(($iIp>>24)) \
            $(($iIp>>16&255)) $(($iIp>>8&255)) $(($iIp&255))
    done
}
maskLen() {
    local i
    for ((i=0; i<32 && ( 1 &  >> (31-i) ) ;i++));do :;done
    printf -v  "%d" $i
}

# The main loop.

while read -a rtLine ;do
    if [ ${rtLine[2]} == "00000000" ] && [ ${rtLine[7]} != "00000000" ] ;then
        hexToInt netInt  ${rtLine[1]}
        hexToInt maskInt ${rtLine[7]}
        if [ $((netInt&maskInt)) == $netInt ] ;then
            for procConnList in /proc/net/{tcp,udp} ;do
                while IFS=': \t\n' read -a conLine ;do
                    if [[ ${conLine[1]} =~ ^[0-9a-fA-F]*$ ]] ;then
                        hexToInt ipInt ${conLine[1]}
                        [ $((ipInt&maskInt)) == $netInt ] && break 3
                    fi
                done < $procConnList
            done
        fi
    fi
done < /proc/net/route 

# And finaly the printout of what's found

maskLen maskBits $maskInt
intToIp addrLine $ipInt $netInt $maskInt
printf -v outForm '%-12s: %%s\n' Interface Address Network Netmask Masklen
printf "$outForm" $rtLine $addrLine $maskBits\ bits

There is a sample of output:

有一个输出示例:

Interface   : eth0
Address     : 192.168.1.32
Network     : 192.168.1.0
Netmask     : 255.255.255.0
Masklen     : 24 bits

Explanation:

解释:

I use integer value of IPV4 in order to check IP & MASK == NETWORK.

我使用 IPV4 的整数值来检查IP & MASK == NETWORK.

I read first /proc/net/routeto find routing configurations, searching for routes reachable without any gateway (gw==000000).

我首先阅读/proc/net/route以查找路由配置,搜索无需任何网关 ( gw==000000)即可到达的路由。

For such a route, I search in all connections (TCP, than UDP if not found in TCP) for connection using thisroute, the first end point is my host address.

对于这样的路由,我在所有连接(TCP,如果在 TCP 中找不到,则比 UDP)中搜索使用路由的连接,第一个端点是我的主机地址。

Nota: This won't work with PPP connections

注意:这不适用于 PPP 连接

Nota2: This won't work on a totally quiet host without any opened network connection. You could do something like echo -ne '' | nc -q 0 -w 1 8.8.8.8 80 & sleep .2 && ./retrieveIp.shfor ensuring that something where found in /proc/net/tcp.

注意2:如果没有任何打开的网络连接,这将无法在完全安静的主机上运行。你可以做一些类似echo -ne '' | nc -q 0 -w 1 8.8.8.8 80 & sleep .2 && ./retrieveIp.sh的事情来确保在/proc/net/tcp.

Nota3, 2016-09.23: New bashversion use >(command)syntax for multiple inline pipefeature. This implie a bug at line 18: a space mustbe present between >and (!!

Nota3, 2016-09.23: 新的bash版本>(command)multiple inline pipefeature使用 语法。这意味着第 18 行的一个错误:和!!之间必须有一个空格!>(

New version with gateway

网关的新版本

There is a little patch: Once you create a file called getIPv4.shby copying previous script, you could paste the following to the command: patch -p0

有一个小补丁:一旦你创建了一个getIPv4.sh通过复制以前的脚本调用的文件,你可以将以下内容粘贴到命令中:patch -p0

--- getIPv4.sh
+++ getIPv4.sh
@@ -35,13 +35,16 @@
                 done < $procConnList
             done
         fi
+    elif [ ${rtLine[1]} == "00000000" ] && [ ${rtLine[7]} == "00000000" ] ;then
+       hexToInt netGw ${rtLine[2]}
     fi
 done < /proc/net/route 

 # And finaly the printout of what's found

 maskLen maskBits $maskInt
-intToIp addrLine $ipInt $netInt $maskInt
-printf -v outForm '%-12s: %%s\n' Interface Address Network Netmask Masklen
+intToIp addrLine $ipInt $netInt $netGw $maskInt
+printf -v outForm '%-12s: %%s\n' \
+       Interface Address Network Gateway Netmask Masklen
 printf "$outForm" $rtLine $addrLine $maskBits\ bits

End with Ctrld, this may output:

以 结尾Ctrld,这可能会输出:

patching file getIPv4.sh

And maybe

有可能

Hunk #1 succeeded at 35 with fuzz 2.

Then re-run your script:

然后重新运行你的脚本:

getIPv4.sh
Interface   : eth0
Address     : 192.168.1.32
Network     : 192.168.1.0
Gateway     : 192.168.1.1
Netmask     : 255.255.255.0
Masklen     : 24 bits

回答by d0n

hers a fancy one i found somewhere in the internet. minorly fixed it up to fit and correctly output tun (vpn) devices.

她是我在互联网上找到的一个花哨的。稍微修复它以适合并正确输出 tun (vpn) 设备。

#!/usr/bin/python
from socket import AF_INET, AF_INET6, inet_ntop
from ctypes import (
    Structure, Union, POINTER, 
    pointer, get_errno, cast,
    c_ushort, c_byte, c_void_p, c_char_p, c_uint, c_int, c_uint16, c_uint32
)
import ctypes.util
import ctypes

class struct_sockaddr(Structure):
     _fields_ = [
        ('sa_family', c_ushort),
        ('sa_data', c_byte * 14),]

class struct_sockaddr_in(Structure):
    _fields_ = [
        ('sin_family', c_ushort),
        ('sin_port', c_uint16),
        ('sin_addr', c_byte * 4)]

class struct_sockaddr_in6(Structure):
    _fields_ = [
        ('sin6_family', c_ushort),
        ('sin6_port', c_uint16),
        ('sin6_flowinfo', c_uint32),
        ('sin6_addr', c_byte * 16),
        ('sin6_scope_id', c_uint32)]

class union_ifa_ifu(Union):
    _fields_ = [
        ('ifu_broadaddr', POINTER(struct_sockaddr)),
        ('ifu_dstaddr', POINTER(struct_sockaddr)),]

class struct_ifaddrs(Structure):
    pass

struct_ifaddrs._fields_ = [
    ('ifa_next', POINTER(struct_ifaddrs)),
    ('ifa_name', c_char_p),
    ('ifa_flags', c_uint),
    ('ifa_addr', POINTER(struct_sockaddr)),
    ('ifa_netmask', POINTER(struct_sockaddr)),
    ('ifa_ifu', union_ifa_ifu),
    ('ifa_data', c_void_p),]

libc = ctypes.CDLL(ctypes.util.find_library('c'))

def ifap_iter(ifap):
    ifa = ifap.contents
    while True:
        yield ifa
        if not ifa.ifa_next:
            break
        ifa = ifa.ifa_next.contents

def getfamaddr(sa):
    family = sa.sa_family
    addr = None
    if family == AF_INET:
        sa = cast(pointer(sa), POINTER(struct_sockaddr_in)).contents
        addr = inet_ntop(family, sa.sin_addr)
    elif family == AF_INET6:
        sa = cast(pointer(sa), POINTER(struct_sockaddr_in6)).contents
        addr = inet_ntop(family, sa.sin6_addr)
    return family, addr

class NetworkInterface(object):
    def __init__(self, name):
        self.name = name
        self.index = libc.if_nametoindex(name)
        self.addresses = {}
    def __str__(self):
        return "%s [index=%d, IPv4=%s, IPv6=%s]" % (
            self.name, self.index,
            self.addresses.get(AF_INET),
            self.addresses.get(AF_INET6))

def get_network_interfaces():
    ifap = POINTER(struct_ifaddrs)()
    result = libc.getifaddrs(pointer(ifap))
    if result != 0:
        raise OSError(get_errno())
    del result
    try:
        retval = {}
        for ifa in ifap_iter(ifap):
            name = ifa.ifa_name
            i = retval.get(name)
            if not i:
                i = retval[name] = NetworkInterface(name)
            try:
                family, addr = getfamaddr(ifa.ifa_addr.contents)
            except ValueError:
                family, addr = None, None
            if addr:
                i.addresses[family] = addr
        return retval.values()
    finally:
        libc.freeifaddrs(ifap)

if __name__ == '__main__':
    print [str(ni) for ni in get_network_interfaces()]

回答by user2856022

ip addr show dev eth0 | grep "inet " | cut -d ' ' -f 6  | cut -f 1 -d '/'

回答by xchange

/proc/net/fib_trieholds the network topography

/proc/net/fib_trie掌握网络拓扑

To simply print the addresses of all adapters:

简单地打印所有适配器的地址:

$ awk '/32 host/ { print f } {f=}' <<< "$(</proc/net/fib_trie)"
127.0.0.1
192.168.0.5
192.168.1.14

To determine the adapter of those addresses (a) consult the adapters' destination networks from /proc/net/route, (b) match those networks with the ones of /proc/net/fib_trieand (c) print the corresponding /32 host addresses listed under those networks.

要确定这些地址的适配器/proc/net/route,请 (a)从 中查询适配器的目标网络,(b) 将这些网络与以下网络匹配,/proc/net/fib_trie以及 (c) 打印在这些网络下列出的相应 /32 主机地址。

Again no pythonunfortunately, but a quite awky bashapproach:

python不幸的是,再次没有,而是一种非常糟糕的bash方法:

#!/bin/bash

ft_local=$(awk '=="Local:" {flag=1} flag' <<< "$(</proc/net/fib_trie)")

for IF in $(ls /sys/class/net/); do
    networks=$(awk '=="'$IF'" && =="00000000" && !="FFFFFFFF" {printf   "\n"}' <<< "$(</proc/net/route)" )
    for net_hex in $networks; do
            net_dec=$(awk '{gsub(/../, "0x& "); printf "%d.%d.%d.%d\n", , , , }' <<< $net_hex)
            mask_dec=$(awk '{gsub(/../, "0x& "); printf "%d.%d.%d.%d\n", , , , }' <<< $net_hex)
            awk '/'$net_dec'/{flag=1} /32 host/{flag=0} flag {a=} END {print "'$IF':\t" a "\n\t'$mask_dec'\n"}' <<< "$ft_local"
    done
done

exit 0

output:

输出:

eth0:     192.168.0.5
          255.255.255.0

lo:       127.0.0.1
          255.0.0.0

wlan0:    192.168.1.14
          255.255.255.0

Known limitation:

已知限制:

This approach does not work reliably for host addresses that share the network with other host addresses. This loss of network uniqueness makes it impossible to determine the correct host address from fib_trie as the order of those addresses does not necessarily match the order of networks of route.

对于与其他主机地址共享网络的主机地址,这种方法不能可靠地工作。这种网络唯一性的丢失使得无法从 fib_trie 确定正确的主机地址,因为这些地址的顺序不一定与路由网络的顺序匹配。

Having said that, I'm not sure why you would want multiple host addresses belonging to the same network in first place. So in most use cases this approach should work just fine.

话虽如此,我不确定为什么您首先需要属于同一网络的多个主机地址。所以在大多数用例中,这种方法应该可以正常工作。

回答by user11135976

ip -j -o -4 addr show dev eth0 | jq .[1].addr_info[0].local