マシンのIPアドレスを取得する


94

この質問は、前に尋ねたローカルコンピュータのIPアドレスの取得-質問とほぼ同じです。しかし、LinuxマシンのIPアドレスを見つける必要があります。

だから:どのように-私はC ++でプログラム的に- 私のアプリケーションが実行されているLinuxサーバーのIPアドレスを検出します。サーバーには少なくとも2つのIPアドレスがあり、特定のアドレス(特定のネットワーク内の1つ(パブリックアドレス))が必要です。

それを行う簡単な関数があると私は確信しています-しかし、どこに?


少しわかりやすくするには:

  • サーバーには明らかに「localhost」があります:127.0.0.1
  • サーバーは内部(管理)IPアドレスを持っています:172.16.xx
  • サーバーは外部(パブリック)IPアドレスを持っています:80.190.xx

アプリケーションをバインドする外部IPアドレスを見つける必要があります。明らかに、INADDR_ANYにバインドすることもできます(実際、それが現在私が行っていることです)。しかし、私はパブリックアドレスを検出したいと思います。


3
ifconfigのpopenをマークダウンするのはなぜですか?それは本当に正しいことです。ライブラリは変更されますが、ifconfigとpopenは常に存在します。
Erik Aronesty、2011年

3
いいえ、ifconfigrouteなどがで廃止されているipコマンド。代わりにそれを使用する必要があります。
アンデルス

現在でもifconfigは、他のどのアプローチよりも多くのアーキテクチャで機能します。したがって、それをipコマンドに変更します。ポペンはまだより良い解決策です。
Erik Aronesty 2016

回答:


104

私はos xでioctlソリューションに問題があることを発見しました(これはPOSIXに準拠しているため、Linuxに似ているはずです)。ただし、getifaddress()を使用すると同じことを簡単に行うことができます。OSx 10.5では私にとって問題なく機能し、以下と同じです。

以下の簡単な例を実行して、マシンのすべてのIPv4アドレスを出力します(getifaddrsが成功した、つまり0を返すことも確認する必要があります)。

IPv6アドレスも表示するように更新しました。

#include <stdio.h>      
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h> 
#include <string.h> 
#include <arpa/inet.h>

int main (int argc, const char * argv[]) {
    struct ifaddrs * ifAddrStruct=NULL;
    struct ifaddrs * ifa=NULL;
    void * tmpAddrPtr=NULL;

    getifaddrs(&ifAddrStruct);

    for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
        if (!ifa->ifa_addr) {
            continue;
        }
        if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4
            // is a valid IP4 Address
            tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
            printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); 
        } else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6
            // is a valid IP6 Address
            tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
            char addressBuffer[INET6_ADDRSTRLEN];
            inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
            printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); 
        } 
    }
    if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);
    return 0;
}

7
+1、退屈なioctlの代わりにこの素晴らしいアプローチを導入してくれてありがとう!ただし、IP6の処理は依然として正しくありません。つまり、sockaddr_in6にキャストする必要があります。つまり、次のようなものですtmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
Andrey

2
@アンドレイ、ありがとう。私は答えを更新しました(ただし、テストする機会がありませんでした)
Twelve47

2
私の場合、ギブしlowlan0インターフェイスします。私が必要とするものは、wlan0またはeth0インターネットに接続されてstrcmpいるものです。
Sudip Bhattarai 2016

28
  1. ソケットを作成します。
  2. 演じる ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

および構造/usr/include/linux/if.hについての情報をお読みください。これにより、システムの各インターフェースのIPアドレスが表示されます。追加のioctlについてもお読みください。ifconfifreq/usr/include/linux/sockios.h


私はそれがあなたにそれがバインドするインターフェースのアドレスを与えるだけだという印象を受けました。
マットジョイナー

@MattJoinerいいえ、manページを見てください。SIOCGIFCONFはすべてのインターフェイスの情報を返します。ここに引用されているmanページの一部:「インターフェース(トランスポート層)アドレスのリストを返します。これは現在、互換性のためにAF_INET(IPv4)ファミリーのアドレスのみを意味します。ユーザーはioctlに引数としてifconf構造体を渡します。 ifc_reqでifreq構造体の配列とバイト数をifc_lenにその長さへのポインタカーネル「が実行されているすべての現在のL3インターフェースアドレスでifreqsを満たす。
ステファン

25

私はjjvainioの答えが好きです。Zan Lnyxが言うように、ローカルルーティングテーブルを使用して、特定の外部ホストへの接続に使用されるイーサネットインターフェイスのIPアドレスを見つけます。接続されたUDPソケットを使用すると、実際にパケットを送信せずに情報を取得できます。このアプローチでは、特定の外部ホストを選択する必要があります。ほとんどの場合、よく知られているパブリックIPでうまくいくはずです。この目的のためにGoogleのパブリックDNSサーバーアドレス8.8.8.8が好きですが、別の外部ホストIPを選択したい場合があります。以下は、完全なアプローチを示すコードです。

void GetPrimaryIp(char* buffer, size_t buflen) 
{
    assert(buflen >= 16);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    assert(sock != -1);

    const char* kGoogleDnsIp = "8.8.8.8";
    uint16_t kDnsPort = 53;
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp);
    serv.sin_port = htons(kDnsPort);

    int err = connect(sock, (const sockaddr*) &serv, sizeof(serv));
    assert(err != -1);

    sockaddr_in name;
    socklen_t namelen = sizeof(name);
    err = getsockname(sock, (sockaddr*) &name, &namelen);
    assert(err != -1);

    const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, buflen);
    assert(p);

    close(sock);
}

2
これにより、プライベートIP 127。*。*。*が得られます。多分私はNATの後ろにいるからですか?
eugene 2011

14

ご存じのとおり、「ローカルIPアドレス」は1つもありません。特定のホストに送信できるローカルアドレスを見つける方法は次のとおりです。

  1. UDPソケットを作成する
  2. ソケットを外部アドレス(最終的にローカルアドレスを受け取るホスト)に接続します。
  3. getsocknameを使用してローカルアドレスを取得する

この方法で行ったことはありませんが、気に入っています。OSルーティングテーブルを自動的に使用し、インターフェイスリストをスキャンするよりもはるかに簡単です。
Zan Lynx

5
これは、外部アドレスがどうなるかを知っていることを前提としています。これは多くの場合、データセンターでは当てはまりません。
Christopher

異なる大陸に割り当てられている3つをいつでも試すことができ(IANAがこれを容易にします)、3つのうち最適な2つを受け入れます。
ジョシュア

9

これには多くの種類のUNIXで作業できるという利点があり、簡単に変更して任意のo / sで作業できます。上記のすべてのソリューションでは、月の満ち欠けに応じてコンパイラエラーが発生します。それを行うための良いPOSIX方法がある瞬間...これを使用しないでください(これが書かれた時点では、そうではありませんでした)。

// ifconfig | perl -ne 'print "$1\n" if /inet addr:([\d.]+)/'

#include <stdlib.h>

int main() {
        setenv("LANG","C",1);
        FILE * fp = popen("ifconfig", "r");
        if (fp) {
                char *p=NULL, *e; size_t n;
                while ((getline(&p, &n, fp) > 0) && p) {
                   if (p = strstr(p, "inet ")) {
                        p+=5;
                        if (p = strchr(p, ':')) {
                            ++p;
                            if (e = strchr(p, ' ')) {
                                 *e='\0';
                                 printf("%s\n", p);
                            }
                        }
                   }
              }
        }
        pclose(fp);
        return 0;
}

このソリューションに感謝します。私にとってはうまく機能し、clプログラムからデータを取得したいだけの多くの状況で非常に便利です
zitroneneis

ifconfigは必要なデータを出力しますが、この方法でプログラムを使用しないことを強くお勧めします。実際にifconfigのソースにアクセスして機能を取得する方が、実行して出力を解析するよりもはるかに優れています。
Zaur Nasibov

1
@MartinMeeser:「inet」と「:」を別々に検索することにより、言語を中立にしました。
Erik Aronesty 2014年

1
次に、ラテン語ベースの文字セットではない他の言語があります。解決策は、コマンドツールを使用しないことですifconfigip今すぐ使うべきです。適切な言語中立的出力を生成する方法がセットにあるLANG環境変数にCのように、:LANG=C ls -l
アンダース

1
ありがとう。簡単で素晴らしいソリューションです!
アフマドアフカンデ

5

私はあなたの質問に決定的な正しい答えがあるとは思いません。代わりに、あなたが望むものに近づく方法の大きな束があります。したがって、それを実行する方法のヒントをいくつか提供します。

マシンに3つ以上のインターフェースがある場合(lo1つとしてカウント)、適切なインターフェースを簡単に自動検出するのに問題があります。ここでは、その方法に関するいくつかのレシピを示します。

たとえば問題は、ホストがNATファイアウォールの背後にあるDMZ内にあり、パブリックIPをプライベートIPに変更してリクエストを転送する場合です。お使いのマシンには10個のインターフェイスがありますが、パブリックインターフェイスに対応するのは1つだけです。

ファイアウォールがソースIPをまったく異なるものに変換するダブルNATを使用している場合、自動検出も機能しません。したがって、デフォルトのルートがパブリックインターフェースを備えたインターフェースに通じているかどうかさえ確信できません。

デフォルトルートを介して検出する

これは私が物事を自動検出するための推奨される方法です

ip r get 1.1.1.1通常のようなものは、デフォルトルートを持つインターフェースを教えてくれます。

これをお気に入りのスクリプト/プログラミング言語で再現したい場合strace ip r get 1.1.1.1は、黄色いレンガの道を使用してください。

とセット /etc/hosts

これは、あなたが管理を続けたい場合の私の推奨事項です

/etc/hostsようなエントリを作成できます

80.190.1.3 publicinterfaceip

次に、このエイリアスpublicinterfaceipを使用してパブリックインターフェイスを参照できます。

悲しいことにhaproxyIPv6でこのトリックを巧みにこなすことはありません

環境を利用する

これは、/etc/hostsあなたがそうでない場合のための良い回避策ですroot

と同じ/etc/hosts。これには環境を使用します。あなたは試すことができます/etc/profile~/.profile、このため。

したがって、プログラムに変数が必要MYPUBLICIPな場合は、次のようなコードを含めることができます(これはCです。自由にC ++を作成してください)。

#define MYPUBLICIPENVVAR "MYPUBLICIP"

const char *mypublicip = getenv(MYPUBLICIPENVVAR);

if (!mypublicip) { fprintf(stderr, "please set environment variable %s\n", MYPUBLICIPENVVAR); exit(3); }

あなたのスクリプト/プログラムを呼び出すことができますので/path/to/your/script、このように

MYPUBLICIP=80.190.1.3 /path/to/your/script

これでも動作します crontabます。

すべてのインターフェースを列挙し、不要なインターフェースを排除する

使えないなら必死の方法 ip

不要なものがわかっている場合は、すべてのインターフェースを列挙し、すべての誤ったインターフェースを無視できます。

これはすでに回答のようですhttps://stackoverflow.com/a/265978/490291、このアプローチようです。

DLNAのようにする

酒に溺れる酒酔い男の道

ネットワーク上のすべてのUPnPゲートウェイを列挙しようとすることができます。これにより、いくつかの "外部"の適切なルートを見つけることができます。これは、デフォルトルートが指し示していないルート上にある場合もあります。

詳細については、https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocolを参照してください

これにより、デフォルトルートが他の場所を指している場合でも、どれが実際のパブリックインターフェイスであるかがわかります。

さらにあります

山と預言者が出会う場所

IPv6ルーターは、適切なIPv6プレフィックスを提供するようにアドバタイズします。プレフィックスを見ると、内部IPかグローバルIPかがわかります。

IGMPまたはIBGPフレームをリッスンして、適切なゲートウェイを見つけることができます。

IPアドレスが2 ^ 32未満です。したがって、LANでそれらすべてにpingを実行するだけでは時間がかかりません。これは、あなたの視点から、インターネットの大部分がどこにあるかに関する統計的なヒントを与えます。ただし、有名なhttps://de.wikipedia.org/wiki/SQL_Slammerよりも少し賢明である必要があります

ICMPやARPも、ネットワークサイドバンド情報の優れたソースです。それもあなたを助けるかもしれません。

イーサネットブロードキャストアドレスを使用して、DHCP(DHCPv6も含む)など、多くの場合に役立つすべてのネットワークインフラストラクチャデバイスに接続できます。

ネットワークデバイスのすべての製造元が、自社のデバイスを自動検出する方法に関する新しいセキュリティホールを忙しく発明しているため、この追加リストはおそらく無限で常に不完全です。これは多くの場合、存在しないはずのパブリックインターフェイスを検出する方法に大きく役立ちます。

'言っ途切れる。でる。


4

Steve Bakerの発言に加えて、SIOCGIFCONF ioctlの説明はnetdevice(7)のマニュアルページにあります

ホスト上のすべてのIPアドレスのリストを取得したら、アプリケーション固有のロジックを使用して不要なアドレスを除外し、IPアドレスが1つ残っていることを確認する必要があります。


4

ハードコーディングしないでください。これは変更可能な種類のことです。多くのプログラムは、設定ファイルを読み込んで、何と言っても何にバインドするかを理解しています。このようにして、将来のある時点でプログラムがパブリックIPではない何かにバインドする必要がある場合に、バインドすることができます。


2

質問はLinuxを指定しているので、マシンのIPアドレスを検出するための私のお気に入りの手法は、netlinkを使用することです。プロトコルNETLINK_ROUTEのネットリンクソケットを作成し、RTM_GETADDRを送信することにより、アプリケーションはすべての使用可能なIPアドレスを含むメッセージを受信します。ここに例を示します

メッセージ処理の一部を簡単に行うには、libmnlが便利です。NETLINK_ROUTEのさまざまなオプション(およびそれらがどのように解析されるか)について詳しく知りたい場合は、カーネルの受信機能だけでなく、iproute2(特にモニターアプリケーション)のソースコードが最適です。rtnetlinkのmanページにも役立つ情報が含まれています。


1

curlと簡単に統合curl www.whatismyip.orgできます。シェルからグローバルIPを取得します。なんらかの外部サーバーに依存していますが、NATの背後にいる場合は常にそうです。


なぜこのサイトを利用しない人が増えるのか理解できません。機械で読み取り可能なページもあるので、情報をスクラップにスクラップする必要はありません。
deft_code 2009

1
プロキシ内で動作している場合、正しいIPを取得できない場合があります。
Jack

1
icanhazip.comはIP のみを返します。bashスクリプトに埋め込むのに最適です!
lukecyca 2011年

ipinfo.io/ipは別の代替手段です。ipinfo.ioをカールすると、ホスト名、地理位置情報などもJSON形式で返します。
ベンダウリング2013


-10

// Use a HTTP request to a well known server that echo's back the public IP address void GetPublicIP(CString & csIP) { // Initialize COM bool bInit = false; if (SUCCEEDED(CoInitialize(NULL))) { // COM was initialized bInit = true;

    // Create a HTTP request object
    MSXML2::IXMLHTTPRequestPtr HTTPRequest;
    HRESULT hr = HTTPRequest.CreateInstance("MSXML2.XMLHTTP");
    if (SUCCEEDED(hr))
    {
        // Build a request to a web site that returns the public IP address
        VARIANT Async;
        Async.vt = VT_BOOL;
        Async.boolVal = VARIANT_FALSE;
        CComBSTR ccbRequest = L"http://whatismyipaddress.com/";

        // Open the request
        if (SUCCEEDED(HTTPRequest->raw_open(L"GET",ccbRequest,Async)))
        {
            // Send the request
            if (SUCCEEDED(HTTPRequest->raw_send()))
            {
                // Get the response
                CString csRequest = HTTPRequest->GetresponseText();

                // Parse the IP address
                CString csMarker = "<!-- contact us before using a script to get your IP address -->";
                int iPos = csRequest.Find(csMarker);
                if (iPos == -1)
                    return;
                iPos += csMarker.GetLength();
                int iPos2 = csRequest.Find(csMarker,iPos);
                if (iPos2 == -1)
                    return;

                // Build the IP address
                int nCount = iPos2 - iPos;
                csIP = csRequest.Mid(iPos,nCount);
            }
        }
    }
}

// Unitialize COM
if (bInit)
    CoUninitialize();

}


10
これは重い、Windowsのみの(Linuxに関する質問)、Webサイトのコンテンツの脆弱な解析を介したローカルコントロールの外にあるサービスに依存する不適切にフォーマットされたソリューションです。この「解決策」のプラス面は見つかりません。
viraptor '18年

1
はぁ!HTML解析では、何が行われているのかについての警告であるコメントも探します。
notlesh
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.