web-dev-qa-db-fra.com

Obtenir des adresses d'interface réseau locales en utilisant uniquement proc?

Comment puis-je obtenir les adresses (IPv4) pour toutes les interfaces réseau en utilisant uniquement proc ? Après une enquête approfondie, j'ai découvert le suivant:

  1. ifconfig utilise SIOCGIFADDR, qui nécessite des sockets ouverts et une connaissance avancée de tous les noms d'interface. Il n'est également documenté dans aucune page de manuel sur Linux.
  2. proc contient /proc/net/dev, mais il s'agit d'une liste de statistiques d'interface.
  3. proc contient /proc/net/if_inet6, qui est exactement ce dont j'ai besoin mais pour IPv6.
  4. En général, 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.
  5. Il y a un appel système appelé getifaddrs , qui est une fonction "magique" que vous attendez de Windows. Il est également implémenté sur BSD. Cependant, il n'est pas très orienté texte, ce qui le rend difficile à utiliser à partir de langages non-C.
37
Matt Joiner

Il n'y a pas d'analogue IPv4 de/proc/net/if_inet6

ifconfig fait:

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")}}}})
6
adobriyan

/proc/net/fib_trie contient la topographie du réseau

Pour imprimer simplement les adresses de tous les adaptateurs:

$ awk '/32 Host/ { print f } {f=$2}' <<< "$(</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), consultez les réseaux de destination des adaptateurs à partir de /proc/net/route, (b) associez ces réseaux à ceux de /proc/net/fib_trie et (c) impriment les adresses d'hôte/32 correspondantes répertoriées sous ces réseaux.

Encore une fois pas de python malheureusement, mais une approche bash assez géniale:

#!/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

production:

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 d'hôte qui partagent le réseau avec d'autres adresses d'hôte. Cette perte d'unicité de réseau rend impossible de déterminer l'adresse d'hôte correcte à 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 sais pas pourquoi vous voudriez en premier lieu plusieurs adresses d'hôte appartenant au même réseau. Donc, dans la plupart des cas d'utilisation, cette approche devrait très bien fonctionner.

16
xchange

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 TCP actuellement ouvertes, ce qui est différent de ce que vous avez demandé, mais cela 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 IP est 192.168.0.121; notez l'arithmétique amusante pour le faire ressortir correctement. :)

14
sarnold

Ma solution pour récupérer la configuration réseau IPv4, en utilisant /proc Uniquement:

Malheureusement, c'est bash ( bash seulement et sans n'importe quelle fourchette), pas python . Mais j'espère que cela sera lisible:

#!/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'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:

J'utilise la valeur entière d'IPV4 afin de vérifier IP & MASK == NETWORK.

J'ai d'abord lu /proc/net/route Pour trouver les configurations de routage, en recherchant les itinéraires 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 d'hôte.

Nota: 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 pouvez faire quelque chose comme echo -ne '' | nc -q 0 -w 1 8.8.8.8 80 & sleep .2 && ./retrieveIp.sh Pour vous assurer que quelque chose se trouve dans /proc/net/tcp.

Nota3, 2016-09.23: Nouvelle version bash utilisez la syntaxe >(command) pour multiple inline pipe fonctionnalité. 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 avez créé un fichier appelé getIPv4.sh En copiant le script précédent, vous pouvez coller ce qui suit dans la commande: 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

Terminer par Ctrld, cela peut produire:

patching file getIPv4.sh

Et peut-être

Hunk #1 succeeded at 35 with fuzz 2.

Réexécutez ensuite 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
10
F. Hauri
ip addr show dev eth0 | grep "inet " | cut -d ' ' -f 6  | cut -f 1 -d '/'
4
user2856022

chat/proc/net/tcp

Obtenez la deuxième colonne, avec le titre "local_address", par ex. "CF00A8C0: 0203"

La partie après ":" est un numéro de port.

Du reste, utilisez les deux derniers (C0) comme un nombre hexadécimal, par ex. C0 est 192, qui est le début de l'adresse dans cet exemple.

J'ai pris ce qui suit dans mes notes il y a quelque temps, à partir d'un point intelligent du net:

L'adresse IP est affichée sous la forme d'un nombre hexadécimal à quatre octets en petit bout; c'est-à-dire que l'octet le moins significatif est répertorié en premier, vous devrez donc inverser l'ordre des octets pour le convertir en adresse IP.

Le numéro de port est un simple nombre hexadécimal à deux octets.

3
klaus thorn

la sienne une fantaisie que j'ai trouvée quelque part sur Internet. légèrement corrigé pour s'adapter et sortir correctement les périphériques 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 __== '__main__':
    print [str(ni) for ni in get_network_interfaces()]
3
d0n
ip -j -o -4 addr show dev eth0 | jq .[1].addr_info[0].local
0
user11135976