47

procのみを使用して、すべてのネットワーク インターフェイスの (IPv4) アドレスを取得するにはどうすればよいですか? 大規模な調査の結果、次のことがわかりました。

  1. ifconfigを使用しますSIOCGIFADDR。これには、開いているソケットと、すべてのインターフェイス名に関する事前の知識が必要です。また、Linux のマニュアル ページにも記載されていません。
  2. procが含まれています/proc/net/devが、これはインターフェース統計のリストです。
  3. procこれは/proc/net/if_inet6まさに私が必要とするものですが、IPv6 の場合です。
  4. 通常、インターフェイスは で簡単に見つけることができますがproc、実際のアドレスは、明示的に接続の一部である場合を除いて、ほとんど使用されません。
  5. と呼ばれるシステム コールがありgetifaddrsます。これは、Windows で見られると思われる非常に「魔法の」機能です。BSDにも実装されています。ただし、あまりテキスト指向ではないため、非 C 言語からの使用は困難です。
4

10 に答える 10

17

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; それを正しく出すための面白い算術に注意してください。:)

于 2011-03-12T08:05:24.623 に答える
14

以下のみを使用して、IPv4ネットワーク構成を取得する私のソリューション/proc

残念ながら、これは ( bash のみでforkなし)です。しかし、これが読みやすいことを願っています:

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

出力のサンプルがあります:

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) で、このルートを使用する接続を検索します。最初のエンドポイントはホスト アドレスです。

注: これは PPP 接続では機能しません

注 2: これは、ネットワーク接続が開かれていない完全に静かなホストでは機能しません。echo -ne '' | nc -q 0 -w 1 8.8.8.8 80 & sleep .2 && ./retrieveIp.shで何かが見つかったことを確認するために、次のようなことを行うことができます/proc/net/tcp

Nota3、2016-09.23: 新しいバージョン では、 feature>(command)の構文が使用されます。これは、18 行目のバグを意味します。と!!の間にスペースが必要です。multiple inline pipe >(

ゲートウェイ付きの新バージョン

小さなパッチがあります: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

で終わるとCtrld、次のように出力される場合があります。

patching file getIPv4.sh

そして多分

Hunk #1 succeeded at 35 with fuzz 2.

次に、スクリプトを再実行します。

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
于 2013-02-06T09:32:25.180 に答える
7

/proc/net/if_inet6 の IPv4 類似物はありません

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")}}}})
于 2011-03-12T12:18:52.997 に答える
5
ip addr show dev eth0 | grep "inet " | cut -d ' ' -f 6  | cut -f 1 -d '/'
于 2014-09-28T01:28:24.580 に答える
4

彼女は私がインターネットのどこかで見つけた派手なものです。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()]
于 2013-12-22T14:29:05.120 に答える
3

cat / proc / net / tcp

見出しが「local_address」の2番目の列を取得します(例:「CF00A8C0:0203」)。

「:」の後の部分はポート番号です。

残りの部分から、最後の2つ(C0)を16進数として使用します。たとえば、C0は192で、この例ではアドレスの先頭です。

ネットのいくつかの賢い点から、少し前に私のメモに次のことを取り入れました:

IPアドレスは、リトルエンディアンの4バイトの16進数として表示されます。つまり、最下位バイトが最初にリストされるため、バイトの順序を逆にしてIPアドレスに変換する必要があります。

ポート番号は、単純な2バイトの16進数です。

于 2012-05-09T11:15:52.683 に答える
-1

それは低音であり、私はおそらくコーナーケースを忘れていますが、/ proc / 1 / net / routeを見ると、ルーティングテーブルがあります。ゲートウェイが0.0.0.0の回線を選択した場合、最初の列はインターフェイスで、2番目の列はネットワークバイト順のIPアドレスの16進表現です(3番目の列はフィルタリングするゲートウェイIPです)。 )。

于 2011-06-07T00:05:00.850 に答える