IPv6ソケットでlistenするとIPv4でも接続可能に

   このエントリをはてなブックマークに登録    2017/3/8-1

「IPv6は自分には関係がない」と思っていたとしても、実は気がつかずにIPv6で通信可能な状態になっていることもあるので注意が必要です。

IPv4射影IPv6アドレスへの対応で、IPv6ソケットがIPv4での通信を行えるように実装されていることを、sshを使って前回紹介しました(IPv4アドレスを含むIPv6アドレス表記)。 前回は、クライアント側(connectを行う側)の例だったので、今回はサーバ側の話です。

IPv4だけで使っているつもりでも、IPv6対応のためにIPv6ソケットでサーバが実装されていれば、知らずにIPv6側でもサーバが稼働していることもあります。 たとえば、IPv4のみでApacheなどのWebサーバを起動していると思っていたら、実はIPv6側でもHTTPの受付をしていたということもあります。 Webサーバを起動している機器で、netstatかlsofかssを実行してみてください。 グローバルなIPv6アドレスが設定されていない機器であっても、気がつかずにlocalhost(::1)やリンクローカルアドレスでHTTPを受け付けているかも知れません。

サンプルコード

では、実際に試してみましょう。このサンプルは、特定のIPアドレスを指定せずにAF_INET6(IPv6)のTCPソケット(SOCK_STREAM)に対してbindを行っています。


#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

int
main()
{
  int sock0;
  struct sockaddr_in client;
  socklen_t len;
  int sock;
  struct addrinfo hints, *res;
  int err;

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_INET6;
  hints.ai_flags = AI_PASSIVE;
  hints.ai_socktype = SOCK_STREAM;
  err = getaddrinfo(NULL, "12345", &hints, &res);
  if (err != 0) {
    printf("getaddrinfo : %s\n", gai_strerror(err));
    return 1;
  }

  /* ソケットの作成 */
  sock0 = socket(res->ai_family, res->ai_socktype, 0);
  if (sock0 < 0) {
    perror("socket");
    return 1;
  }

  if (bind(sock0, res->ai_addr, res->ai_addrlen) != 0) {
    perror("bind");
    return 1;
  }

  freeaddrinfo(res); /* addrinfo構造体を解放 */

  /* TCPクライアントからの接続要求を待てる状態にする */
  listen(sock0, 5);

  /* TCPクライアントからの接続要求を受け付ける */
  len = sizeof(client);
  sock = accept(sock0, (struct sockaddr *)&client, &len);

  /* 6文字送信 */
  write(sock, "HELLO\n", 6);

  /* TCPセッションの終了 */
  close(sock);

  /* listen するsocketの終了 */
  close(sock0);

  return 0;
}


IPV6_V6ONLYがデフォルトでOFFになっていない環境でこのプログラムを実行すると、IPv4のTCPで12345番ポートに接続しても、IPv6のTCPで12345番ポートに接続しても接続できてしまいます。

試しに、telnet ::1 12345 と、telnet 127.0.0.1 12345をやってみてください。

IPv4とIPv6の両方でTCPの受付が行われていることは、他の方法でも確認できます。 たとえば、このサーバプログラムを実行中に「netstat -na」をすると次のような結果を含んでいます(このサンプルはmacOSで試しました)。

Proto部分が「tcp46」となっており、IPv4とIPv6の両方で受け付けているのがわかります。AF_INETで作ったソケットであれば、この部分が「tcp4」となり、IPV6_V6ONLYを有効にした状態のAF_INET6ソケットであれば「tcp6」になります。


Active Internet connections (including servers)
Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)   
tcp46      0      0  *.12345                *.*                    LISTEN     

draft-itojun-v6ops-v4mapped-harmful

さらに詳しい情報を知りたい方は、IPv4射影IPv6アドレスの危険性に関しての故itojunさんが指摘したdraft-itojun-v6ops-v4mapped-harmful-02をご覧ください。

   このエントリをはてなブックマークに登録