Obtenir des adresses d'interface réseau locales en utilisant seulement proc?
Comment puis-je obtenir les adresses (IPv4) pour toutes les interfaces réseau en utilisant seulement proc ? après une enquête approfondie, j'ai découvert ce qui suit:
-
ifconfig
utiliseSIOCGIFADDR
, ce qui nécessite des sockets ouverts et une connaissance préalable de tous les noms d'interface. Il n'est pas non plus documenté dans les pages de manuel de Linux. -
proc
contient/proc/net/dev
, mais ceci est une liste de statistiques d'interface. -
proc
contient/proc/net/if_inet6
, qui est exactement ce dont j'ai besoin mais pour IPv6. - généralement , les interfaces sont faciles à trouver dans
proc
, mais les adresses réelles sont très rarement utilisées, sauf lorsqu'elles font explicitement partie d'une connexion. - il y a un appel système appelé
getifaddrs
, ce qui est une fonction" magique " à laquelle on s'attendrait voir dans Windows. Il est également mis en œuvre sur BSD. Cependant, il n'est pas très axé sur le texte, ce qui le rend difficile à utiliser à partir de langues Non-C.
8 réponses
Il n'y a pas d'IPv4 analogique de /proc/net/if_inet6
ifconfig n':
fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)
ioctl(fd, SIOCGIFCONF, ...)
vous obtiendrez quelque chose comme ceci:
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")}}}})
vous pouvez trouver la sortie de ip addr show
plus facile à analyser que la sortie d'autres outils:
$ 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
une autre option est le fichier /proc/net/tcp
. Il montre toutes les sessions TCP actuellement ouvertes, ce qui est différent de ce que vous avez demandé, mais pourrait être suffisant.
$ 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]
mon adresse IP est 192.168.0.121
; notez l'arithmétique drôle pour le faire sortir à droite. :)
/proc/net/fib_trie
tient la topographie du réseau
pour imprimer simplement les adresses de tous les adaptateurs:
$ awk '/32 host/ { print f } {f=}' <<< "$(</proc/net/fib_trie)"
127.0.0.1
192.168.0.5
192.168.1.14
pour déterminer l'adaptateur de ces adresses (a) consulter les réseaux de destination des adaptateurs de /proc/net/route
, (b) apparier ces réseaux avec ceux de /proc/net/fib_trie
et (c) imprimer les adresses hôtes correspondantes /32 énumérées sous ces réseaux.
de nouveau pas python
malheureusement, mais un tout awky bash
approche:
#!/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
sortie:
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
limitation connue:
cette approche ne fonctionne pas de manière fiable pour les adresses hôtes qui partagent le réseau avec d'autres adresses hôtes. Cette perte d'unicité du réseau rend impossible de déterminer l'adresse correcte de l'hôte à partir de fib_trie car l'ordre de ces adresses ne correspond pas nécessairement à l'ordre des réseaux de route.
Cela dit, Je ne suis pas sûr que vous vouliez plusieurs adresses de serveurs appartenant au même réseau en premier lieu. Donc, dans la plupart des cas, cette approche devrait fonctionner parfaitement.
ma solution pour récupérer la configuration du réseau IPv4, en utilisant /proc
seulement:
malheureusement, c'est bash ( bash seulement et sans n'importe quelle fourche), pas python . Mais j'espère que ce sera lisible:
#!/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
il y a un échantillon de sortie:
Interface : eth0
Address : 192.168.1.32
Network : 192.168.1.0
Netmask : 255.255.255.0
Masklen : 24 bits
explication:
I utilisez la valeur entière de IPV4 pour vérifier IP & MASK == NETWORK
.
j'ai lu en premier /proc/net/route
pour trouver des configurations de routage, cherchant des routes accessibles sans aucune passerelle ( gw==000000
).
pour une telle route, je recherche dans toutes les connexions (TCP, que UDP si non trouvé dans TCP) pour la connexion en utilisant cette route, le premier point final est mon adresse hôte.
Note: cela ne fonctionnera pas avec les connexions PPP
Nota2: cela ne fonctionnera pas sur un hôte totalement silencieux sans connexion réseau ouverte.
Vous pourriez faire quelque chose comme echo -ne '' | nc -q 0 -w 1 8.8.8.8 80 & sleep .2 && ./retrieveIp.sh
pour s'assurer que quelque chose se trouve dans /proc/net/tcp
.
Nota3, 2016-09.23: Nouveau bash Version utiliser >(command)
syntaxe pour multiple inline pipe
caractéristique . Cela implique un bug à la ligne 18: un espace doit être présent entre >
et (
!!
nouvelle version avec passerelle
il y a un petit patch: une fois que vous créez un fichier appelé getIPv4.sh
en copiant le script précédent, vous pouvez coller la commande suivante: 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
se termine par Ctrl "1519620920 d , ce qui peut donner:
patching file getIPv4.sh
et peut-être
Hunk #1 succeeded at 35 with fuzz 2.
puis relancez votre 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
ip addr show dev eth0 | grep "inet " | cut -d ' ' -f 6 | cut -f 1 -d '/'
cat / proc / net /tcp
obtenir la deuxième colonne, avec le titre "local_address", p.ex. "CF00A8C0: 0203 "
la partie après": "est un numéro de port.
pour le reste, utilisez les deux derniers (C0) comme un nombre hexadécimal, par exemple C0 est 192, ce qui est le début de l'adresse dans cet exemple.
a pris dans Mes notes il y a un certain temps, à partir d'un point intelligent dans le net:
l'adresse IP est affiché sous la forme d'un nombre hexadécimal de quatre octets de little-endian; qui est, l'octet le moins significatif est répertorié en premier, vous devrez donc Inverser l'ordre des octets pour les convertir en adresse IP.
le numéro de port est un simple numéro hexadécimal de deux octets.
c'est une fantaisie que j'ai trouvée quelque part sur internet. minorly fixé il jusqu'à l'ajustement et correctement sortie TUN (vpn) appareils.
#!/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()]
c'est bass-ackwards et j'oublie probablement un cas de coin, mais si vous regardez /proc/1/net/route, qui a votre table de routage. Si vous sélectionnez des lignes pour lesquelles la passerelle est 0.0.0.0, la première colonne est l'interface et la deuxième colonne est la représentation hexadécimale de votre adresse IP, dans l'ordre des octets réseau (et la troisième colonne est l'adresse ip passerelle sur laquelle vous voulez filtrer).