Получить адреса локального сетевого интерфейса, используя только proc?
Как я могу получить адреса (IPv4) для всех сетевых интерфейсов, используя только proc? После некоторого обширного расследования у меня есть обнаружил следующее:
-
ifconfig
использует SIOCGIFADDR
, для которого требуются открытые сокеты и знание всех имен интерфейсов. Он также не документируется на каких-либо страницах руководства на Linux.
-
proc
содержит /proc/net/dev
, но это список статистики интерфейса.
-
proc
содержит /proc/net/if_inet6
, что именно то, что мне нужно, но для IPv6.
- В общем случае интерфейсы легко найти в
proc
, но фактические адреса используются очень редко, за исключением случаев, когда явная часть какого-либо соединения.
- Существует системный вызов
getifaddrs
, который является очень "волшебной" функцией, которую вы ожидаете увидеть в Windows. Он также реализован на BSD. Однако он не очень ориентирован на текст, что затрудняет использование с языков, отличных от C.
Ответы
Ответ 1
Нет аналога IPv4/proc/net/if_inet6
ifconfig делает:
fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)
ioctl(fd, SIOCGIFCONF, ...)
Вы получите что-то вроде этого:
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")}}}})
Ответ 2
Вы можете найти результат 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
Другим вариантом является файл /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]
Мой IP 192.168.0.121
; обратите внимание на смешную арифметику, чтобы сделать ее правдой.:)
Ответ 3
Мое решение для получения сетевой конфигурации IPv4, используя только /proc
:
К сожалению, это bash ( bash только и без какой-либо вилки), а не python. Но я надеюсь, что это будет читаемо:
#!/bin/bash
# ip functions that set variables instead of returning to STDOUT
hexToInt() {
printf -v $1 "%d\n" 0x${2:6:2}${2:4:2}${2:2:2}${2:0:2}
}
intToIp() {
local var=$1 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 & $2 >> (31-i) ) ;i++));do :;done
printf -v $1 "%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 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
Имеется образец вывода:
Interface : eth0
Address : 192.168.1.32
Network : 192.168.1.0
Netmask : 255.255.255.0
Masklen : 24 bits
Пояснение:
Я использую целочисленное значение IPV4, чтобы проверить IP & MASK == NETWORK
.
Сначала я прочитал /proc/net/route
, чтобы найти конфигурации маршрутизации, поиск маршрутов, доступных без какого-либо шлюза (gw==000000
).
Для такого маршрута я выполняю поиск во всех соединениях (TCP, чем UDP, если не найден в TCP) для соединения с использованием маршрута this, первая конечная точка - это адрес моего хоста.
Nota: это не будет работать с соединениями PPP
Nota2: Это не будет работать на полностью спокойном хосте без какого-либо открытого сетевого подключения.
Вы можете сделать что-то вроде echo -ne '' | nc -q 0 -w 1 8.8.8.8 80 & sleep .2 && ./retrieveIp.sh
для обеспечения того, что где-то найдено в /proc/net/tcp
.
Nota3, 2016-09.23: New bash используйте синтаксис >(command)
для функции multiple inline pipe
. Это подразумевает ошибку в строке 18: между >
и (
!! <пробел должен
Новая версия с шлюзом
Есть небольшой патч: после создания файла с именем getIPv4.sh
, скопировав предыдущий script, вы можете вставить в команду следующее: 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 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
Завершить с Ctrl d, это может вывести:
patching file getIPv4.sh
И, может быть,
Hunk #1 succeeded at 35 with fuzz 2.
Затем запустите свой 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
Ответ 4
ip addr show dev eth0 | grep "inet " | cut -d ' ' -f 6 | cut -f 1 -d '/'
Ответ 5
/proc/net/fib_trie
содержит топографию сети
Чтобы просто распечатать адреса всех адаптеров:
$ awk '/32 host/ { print f } {f=$2}' <<< "$(</proc/net/fib_trie)"
127.0.0.1
192.168.0.5
192.168.1.14
Чтобы определить адаптер этих адресов (a), обратитесь к сетям назначения адаптеров из /proc/net/route
, (b) сопоставьте эти сети с тегами /proc/net/fib_trie
и (c) распечатайте соответствующие /32 адреса хоста, указанные в эти сети.
Снова нет python
, к сожалению, но довольно сложный bash
подход:
#!/bin/bash
ft_local=$(awk '$1=="Local:" {flag=1} flag' <<< "$(</proc/net/fib_trie)")
for IF in $(ls /sys/class/net/); do
networks=$(awk '$1=="'$IF'" && $3=="00000000" && $8!="FFFFFFFF" {printf $2 $8 "\n"}' <<< "$(</proc/net/route)" )
for net_hex in $networks; do
net_dec=$(awk '{gsub(/../, "0x& "); printf "%d.%d.%d.%d\n", $4, $3, $2, $1}' <<< $net_hex)
mask_dec=$(awk '{gsub(/../, "0x& "); printf "%d.%d.%d.%d\n", $8, $7, $6, $5}' <<< $net_hex)
awk '/'$net_dec'/{flag=1} /32 host/{flag=0} flag {a=$2} END {print "'$IF':\t" a "\n\t'$mask_dec'\n"}' <<< "$ft_local"
done
done
exit 0
выход:
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
Известное ограничение:
Этот подход не работает надежно для адресов хостов, которые совместно используют сеть с другими адресами хоста. Эта потеря уникальности сети делает невозможным определение правильного адреса хоста из fib_trie, поскольку порядок этих адресов не обязательно соответствует порядку сетей маршрута.
Сказав это, я не уверен, почему вы хотели бы, чтобы на одном месте находилось несколько адресов хостов, принадлежащих одной и той же сети. Поэтому в большинстве случаев этот подход должен работать нормально.
Ответ 6
cat/proc/net/tcp
Получите второй столбец с заголовком "local_address", например. "CF00A8C0: 0203"
Часть после ":" - номер порта.
Из остальных используйте последние два (C0) в качестве шестнадцатеричного числа, например. C0 - 192, что является началом адреса в этом примере.
Некоторое время назад я использовал несколько заметок в моих заметках: от умной точки в сети:
IP-адрес отображается в виде шестизначного четырехбайтового шестнадцатеричного числа;
то есть наименее значимый байт указан первым,
поэтому вам нужно будет изменить порядок байтов, чтобы преобразовать его в IP-адрес.
Номер порта - это простое двухбайтное шестнадцатеричное число.
Ответ 7
ее фантазия, которую я нашел где-то в Интернете. незначительно исправил его до соответствия и правильно выводит устройства 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()]
Ответ 8
Это бас-басы, и я, вероятно, забываю о углу, но если вы посмотрите на /proc/ 1/net/route, у вас есть таблица маршрутизации. Если вы выбираете строки, для которых шлюз 0.0.0.0, первый столбец является интерфейсом, а второй столбец представляет собой шестнадцатеричное представление вашего IP-адреса в сетевом порядке байтов (а третий столбец - это шлюз ip, который вы хотите фильтровать на).