自分のIPアドレスを得る

ネットワーク通信を行う場合、プロトコルによっては自分のIPアドレスを相手に通知してあげる必要がある場合がある。

こういうときに、IPアドレスを得るためにgethostname()が返した名前でgethostbyname()を呼ぶ、という方法がとられているのを散見するが、これはダメだ。localhostや自分が設定した名前が帰ってきて、「127.0.0.1」になってしまうことがあるから。

また、特定のホストと通信する場合に、自分の側で使われるIPアドレスを知りたいという場合には使えない(listenするインターフェースを特定する時など)。

自力でルーティングしてたら何のためのネットワークスタックか分からないので、いい方法はないかなあ…と探していたら、見つかったのがこの投稿

要約すると、ダミーのUDPソケットを作り、それを相手ホストにconnect()し、getsockname()で自分のアドレスを得る、と言う方法。
connectのMANPAGEによると、UDPソケットをconnectすると、特に何もパケットが送出されることはなく、そのソケットのデフォルトのパケット送信先/唯一の受信先となる、という仕様があるのだそうだ。初めて知った。

なお、上記の投稿では、「外向け」のインターフェースが欲しい、ということなので、適当な(多分外であろう)IPアドレスにconnectしているが、普通に通信相手が分かっていればそれを指定したほうが良いだろう。

perlで実装してみる。IPアドレスやポート番号はダミー。必要な場合は適切なものに置き換えて。

use Socket;
socket(SOCKET, PF_INET, SOCK_DGRAM, 0);
my $host_addr = pack_sockaddr_in(9999, inet_aton(”192.168.0.1”));
connect(SOCKET, $host_addr); 
my @sock_addr = unpack_sockaddr_in(getsockname(SOCKET));
my $local_ip = inet_ntoa($sock_addr[1]);
print $local_ip,”¥n”;
close(SOCKET);