86


51

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

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

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

'' '' '

物事をもう少し明確にするには:

  • サーバーは明らかに "localhost"を持っているでしょう:127.0.0.1

  • サーバーは内部(管理)IPアドレスを持っています:172.16.x.x

  • サーバーは外部(パブリック)IPアドレスを持ちます。80.190.x.x

アプリケーションをバインドするための外部IPアドレスを見つける必要があります。 もちろん、私はINADDR_ANYにバインドすることもできます(そして実際はそれが現時点で私がしていることです)。 私は、公の住所を検出することを好むでしょう。

12 Answer


96


私はioctlソリューションがos x(POSIXに準拠しているのでLinuxに似ているはずです)で問題があることを発見しました。 しかしgetifaddress()はあなたに同じことを簡単にやらせるでしょう、それはos x 10.5で私のためにうまく働きます、そして以下で同じであるべきです。

以下の簡単な例を実行しました。これはマシンのすべてのIPv4アドレスを表示します(getifaddrsが成功したかどうか、つまり0を返すことも確認してください)。

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

#include #include #include #include #include #include

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

getifaddrs(

for(ifa = ifAddrStruct; ifa!= NULL; ifa = ifa-> ifa_next){if(!ifa-> ifa_addr){続行; if(ifa-> ifa_addr-> sa_family == AF_INET){// IP4であることを確認する// //有効なIP4アドレスであるtmpAddrPtr =


28


  1. ソケットを作成してください。

  2. `ioctl(、SIOCGIFCONF、(struct ifconf)を実行してください。

ifconf`と ifreq`構造体についての情報は `/ usr / include / linux / if.h`を読んでください。 これにより、システム上の各インタフェースのIPアドレスがわかります。 追加のioctlについては `/ usr / include / linux / sockios.h 'も読んでください。


22


私は jjvainio’s answerが好きです。 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(

int err = connect(sock、(const sockaddr *)

sockaddr_in名。 socklen_t namelen = sizeof(name); err = getsockname(sock、(sockaddr *)

const char * p = inet_ntop(AF_INET、

閉じる(靴下)。 }


13


ご存知のように、単一の「ローカルIPアドレス」のようなものはありません。 これは特定のホストに送信できるローカルアドレスを見つける方法です。

  1. UDPソケットを作成する

  2. ソケットを外部アドレス(最終的にローカルアドレスを受け取るホスト)に接続します。

  3. ローカルアドレスを取得するにはgetsocknameを使用してください。


8


これには多くの種類のunixで作業するという利点があります…​そしてあなたはどんなo / sで作業するためにそれを自明に修正することができます。 上記の解決策はすべて、月の満ち欠けに応じてコンパイラエラーを出します。 現時点でそれを行うための良いPOSIXの方法があります…​ これを使用しないでください(これが書かれた時点では、そうではありませんでした)。

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

#含める

int main(){setenv( "LANG"、 "C"、1); FILE * fp = popen( "ifconfig"、 "r"); if(fp){char * p = NULL、* e; size_t n; while((getline(
                            ++p;
if(e = strchr(p、 '')){* e = '\ 0'; printf( "%s \ n"、p); pclose(fp);}}}}} 0を返します。 }


4


Steve Bakerが述べたことに加えて、SIOCGIFCONF ioctlの説明は netdevice(7)のmanページにあります。

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


4


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


4


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

マシンが3つ以上のインターフェースを持っている場合( `lo`は1つとして数える)、正しいインターフェースを簡単に自動検出するのに問題があるでしょう。 これを行う方法についてのいくつかのレシピがあります。

たとえば、問題は、パブリックIPアドレスをプライベートIPアドレスに変更して要求を転送するNATファイアウォールの背後のDMZにホストがある場合です。 あなたのマシンは10個のインターフェースを持っているかもしれませんが、公開されているものに対応するものはひとつだけです。

ファイアウォールが送信元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`を使うことができます。

_ 悲しいことに、「haproxy」はIPv6でこのトリックを理解していません _

環境を利用する

_ これは、 root`でない場合の / etc / hosts`の良い回避策です _

/ 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が好きですか

_ 酒にhimselfれようとする酒に酔った男の道 _

あなたはあなたのネットワーク上のすべてのUPnPゲートウェイを列挙しようと試みることができます、そしてこの方法はいくつかの「外部の」事のために適切なルートを見つけます。 これはあなたのデフォルトルートが指していないルート上にあるかもしれません。

これについての詳細はおそらくhttps://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocolをご覧ください。

たとえあなたのデフォルトルートが他の場所を指していたとしても、これはあなたにどれがあなたの本当のパブリックインターフェースであるかの良い印象を与えます。

もっとあります

_ 山が預言者と出会う場所 _

IPv6ルーターは、正しいIPv6プレフィックスを提供するために自分自身をアドバタイズします。 プレフィックスを見れば、内部IPがあるのか​​グローバルIPがあるのか​​についてのヒントが得られます。

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

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

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

Ethernet Broadcastアドレスを使用すると、DHCP(DHCPv6も)などのように、多くの場合に役立つネットワークインフラストラクチャデバイスすべてに連絡できます。

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

'言っ途切れる。 Out.


1


あなたはcurlといくらか簡単に統合することができます:シェルからの `curl www.whatismyip.org`はあなたのあなたのグローバルIPを手に入れるでしょう。 あなたはある種の外部サーバーにある程度依存していますが、あなたがNATの背後にいるのであればあなたはいつもそうなるでしょう。


1


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

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