Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 18982] ファイルの概要

このコミットは、Go言語の標準ライブラリであるnetパッケージ内のsrc/pkg/net/ipsock_posix.goファイルに対する変更です。このファイルは、POSIX互換システム(Linux, macOSなど)におけるIPソケットの低レベルな操作、特にIPv4およびIPv6スタックの機能検出に関連するコードを含んでいます。具体的には、probeIPv6Stack関数が変更されており、これはシステムがIPv6通信をサポートしているか、またIPv4-mapped IPv6アドレスを介した通信をサポートしているかを検出するために使用されます。

コミット

このコミットは、デュアルIPスタックノード上でのアドレスファミリー選択において、IPv6の機能テストをより適切にするためのものです。GoのnetパッケージがIPv6専用通信にはIPV6_V6ONLY=1を、IPv4とIPv6の両方に対応する通信にはIPV6_V6ONLY=0を厳密に使用している現状に合わせて、機能テストも同様の挙動をするように修正されました。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/a05ffd85aa6f8cc532bd50df8dbdee8e8040afcc

元コミット内容

net: make IPv6 capability test more suitable for address family selection on the dual IP stack node

For now we strictly use IPV6_V6ONLY=1 for IPv6-only communications
and IPV6_V6ONLY=0 for both IPv4 and IPv6 communications. So let the
capability test do the same.

LGTM=iant
R=golang-codereviews, gobot, iant
CC=golang-codereviews
https://golang.org/cl/80140044

変更の背景

Goのnetパッケージは、ネットワーク通信を行う際に、システムのIPv4およびIPv6のサポート状況を適切に判断する必要があります。特に、IPv6ソケットがIPv4トラフィックも処理できる「デュアルスタック」モードで動作するかどうかは、IPV6_V6ONLYソケットオプションによって制御されます。

以前のprobeIPv6Stack関数では、IPv6通信能力とIPv4-mappedアドレス通信能力をテストする際に、常にIPV6_V6ONLYオプションを0(デュアルスタックモードを許可)に設定していました。しかし、Goのnetパッケージの内部ロジックでは、IPv6専用の通信を行う場合にはIPV6_V6ONLY=1を、IPv4とIPv6の両方に対応する通信を行う場合にはIPV6_V6ONLY=0を厳密に使い分けていました。

この不一致が問題でした。機能テストが常にデュアルスタックモードで実行されると、システムがIPV6_V6ONLY=1(IPv6専用)のソケットでIPv6通信をサポートしているかどうかを正確に判断できませんでした。例えば、あるシステムがIPv6専用ソケットではIPv6通信をサポートするが、デュアルスタックソケットでは何らかの理由で問題がある場合、以前のテストではその問題を見落とす可能性がありました。

このコミットの目的は、Goのnetパッケージがソケットオプションをどのように使用しているかという内部的な方針と、IPv6機能の検出ロジックとの間に一貫性を持たせることです。これにより、Goアプリケーションがシステムのネットワーク機能をより正確に検出し、適切なアドレスファミリーを選択できるようになります。

前提知識の解説

IPv6とIPv4-mapped IPv6アドレス

  • IPv6アドレス: 128ビット長のアドレスで、2001:0db8:85a3:0000:0000:8a2e:0370:7334のように表記されます。IPv4アドレス空間の枯渇に対応するために導入されました。
  • IPv4-mapped IPv6アドレス: IPv6アドレス空間内でIPv4アドレスを表現するための特殊な形式です。::ffff:c000:0280::ffff:192.0.2.128に相当)のように表記され、IPv6ソケットがIPv4通信を処理できるようにするために使用されます。これにより、アプリケーションは単一のIPv6ソケットでIPv4とIPv6の両方のクライアントからの接続を受け入れることができます。

デュアルスタックノード

デュアルスタックノードとは、IPv4とIPv6の両方のプロトコルスタックを同時に実行できるホストまたはルーターのことです。これにより、IPv4ネットワークとIPv6ネットワークの両方に接続し、両方のプロトコルで通信を行うことができます。多くの現代のオペレーティングシステムはデフォルトでデュアルスタックをサポートしています。

IPV6_V6ONLYソケットオプション

IPV6_V6ONLYは、IPv6ソケットの挙動を制御するためのソケットオプションです。setsockoptシステムコールを使用して設定されます。

  • IPV6_V6ONLY1(true)に設定した場合:

    • そのIPv6ソケットはIPv6パケットのみを処理します。
    • IPv4-mapped IPv6アドレスを介したIPv4通信は受け付けません。
    • これにより、IPv4アプリケーションとIPv6アプリケーションが同じポートに同時にバインドできる可能性があります(ただし、これはOSの実装に依存します)。
    • Goのnetパッケージでは、IPv6専用の通信を行う場合にこの設定を使用します。
  • IPV6_V6ONLY0(false)に設定した場合:

    • そのIPv6ソケットは、IPv6パケットとIPv4-mapped IPv6アドレスを介したIPv4パケットの両方を処理します。
    • これにより、単一のIPv6ソケットでIPv4とIPv6の両方のトラフィックを処理できるため、アプリケーションの設計が簡素化されます。
    • Goのnetパッケージでは、IPv4とIPv6の両方に対応する通信を行う場合にこの設定を使用します。

このオプションのデフォルト値はオペレーティングシステムによって異なります(Linuxでは通常0、Windowsでは通常1)。そのため、クロスプラットフォームで一貫した挙動を保証するためには、アプリケーションコードで明示的に設定することが推奨されます。

Goのnetパッケージにおけるソケットの挙動

Goのnetパッケージは、抽象化されたネットワークインターフェースを提供し、基盤となるOSのソケットAPIをラップしています。netパッケージは、アプリケーションが指定したネットワークタイプ(例: tcp, tcp4, tcp6)に基づいて、適切なソケットを作成し、IPV6_V6ONLYなどのソケットオプションを内部的に設定します。

probeIPv6Stack関数は、Goランタイムの起動時や必要に応じて呼び出され、現在のシステムがどのようなIPv6機能をサポートしているかを判断します。この情報は、その後のネットワーク接続の確立やアドレス解決のロジックに影響を与えます。

技術的詳細

変更はsrc/pkg/net/ipsock_posix.goファイルのprobeIPv6Stack関数内で行われました。この関数は、システムがIPv6通信をサポートしているか(supportsIPv6)、およびIPv4-mapped IPv6アドレスを介した通信をサポートしているか(supportsIPv4map)を検出します。

以前の実装では、probesという匿名構造体のスライスが定義されており、IPv6通信能力とIPv4-mappedアドレス通信能力をテストするためのローカルアドレス(laddr)が設定されていました。これらのテストソケットを作成した後、syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)という行で、常にIPV6_V6ONLYオプションを0に設定していました。これは、両方のテストケースでデュアルスタックモードを許可することを意味します。

このコミットでは、probes構造体にvalue intフィールドが追加されました。このvalueフィールドは、それぞれのプローブ(テスト)においてIPV6_V6ONLYに設定すべき値を保持します。

  • IPv6通信能力のテスト: laddr: TCPAddr{IP: ParseIP("::1")}(IPv6ループバックアドレス)のプローブに対して、value: 1が設定されました。これは、このテストではIPV6_V6ONLY=1(IPv6専用モード)でソケットを作成し、純粋なIPv6通信がサポートされているかを検証することを意味します。
  • IPv4-mappedアドレス通信能力のテスト: laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}(IPv4ループバックアドレス)のプローブに対して、value: 0が設定されました。これは、このテストではIPV6_V6ONLY=0(デュアルスタックモード)でソケットを作成し、IPv4-mappedアドレスを介した通信がサポートされているかを検証することを意味します。

そして、ソケットオプションを設定する行がsyscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value)に変更されました。これにより、各プローブのvalueフィールドに格納された値が動的にIPV6_V6ONLYオプションに適用されるようになりました。

この変更により、Goのnetパッケージは、IPv6専用通信のテストとデュアルスタック通信のテストを、それぞれGoが内部的に使用するIPV6_V6ONLYの設定に合わせて実行するようになります。これにより、システムのIPv6機能の検出がより正確になり、Goアプリケーションがネットワークインターフェースをより適切に利用できるようになります。特に、デュアルスタック環境において、IPv6専用ソケットとデュアルスタックソケットの挙動の違いを正確に把握することが可能になります。

コアとなるコードの変更箇所

--- a/src/pkg/net/ipsock_posix.go
+++ b/src/pkg/net/ipsock_posix.go
@@ -40,12 +40,13 @@ func probeIPv4Stack() bool {
 func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
 	var probes = []struct {
 		laddr TCPAddr
+		value int
 		ok    bool
 	}{
 		// IPv6 communication capability
-		{TCPAddr{IP: ParseIP("::1")}, false},
+		{laddr: TCPAddr{IP: ParseIP("::1")}, value: 1},
 		// IPv6 IPv4-mapped address communication capability
-		{TCPAddr{IP: IPv4(127, 0, 0, 1)}, false},
+		{laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0},
 	}
 
 	for i := range probes {
@@ -54,7 +55,7 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
 			continue
 		}
 		defer closesocket(s)
-		syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
+		syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value)
 		sa, err := probes[i].laddr.sockaddr(syscall.AF_INET6)
 		if err != nil {
 			continue

コアとなるコードの解説

  1. probes構造体へのvalueフィールドの追加:

    var probes = []struct {
        laddr TCPAddr
        value int // <-- 追加されたフィールド
        ok    bool
    }{
    

    probesスライス内の匿名構造体にvalueというint型のフィールドが追加されました。このフィールドは、各プローブ(テストケース)でIPV6_V6ONLYソケットオプションに設定する値を保持するために使用されます。

  2. IPv6通信能力テストのvalue設定:

    // IPv6 communication capability
    -{TCPAddr{IP: ParseIP("::1")}, false},
    +{laddr: TCPAddr{IP: ParseIP("::1")}, value: 1}, // <-- value: 1 に変更
    

    IPv6ループバックアドレス::1を用いたIPv6通信能力のテストケースにおいて、value1に設定されました。これは、このテストソケットがIPV6_V6ONLY=1(IPv6専用)として動作することを意味します。これにより、純粋なIPv6通信がシステムで適切に機能するかどうかが、GoがIPv6専用ソケットを使用する際と同じ条件でテストされます。

  3. IPv4-mappedアドレス通信能力テストのvalue設定:

    // IPv6 IPv4-mapped address communication capability
    -{TCPAddr{IP: IPv4(127, 0, 0, 1)}, false},
    +{laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0}, // <-- value: 0 に変更
    

    IPv4ループバックアドレス127.0.0.1を用いたIPv4-mappedアドレス通信能力のテストケースにおいて、value0に設定されました。これは、このテストソケットがIPV6_V6ONLY=0(デュアルスタックモード)として動作することを意味します。これにより、IPv4-mappedアドレスを介したIPv4通信がシステムで適切に機能するかどうかが、Goがデュアルスタックソケットを使用する際と同じ条件でテストされます。

  4. SetsockoptInt呼び出しの変更:

    -		syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
    +		syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value) // <-- probes[i].value を使用
    

    ソケットオプションを設定するsyscall.SetsockoptIntの最後の引数が、ハードコードされた0からprobes[i].valueに変更されました。これにより、各プローブのvalueフィールドに定義された値が動的にIPV6_V6ONLYオプションに適用されるようになり、テストの意図とGoの内部的なソケット利用方針との一貫性が保たれます。

これらの変更により、probeIPv6Stack関数は、システムのIPv6機能(純粋なIPv6通信とIPv4-mappedアドレスを介した通信)を、Goのnetパッケージが実際にソケットを使用する際のIPV6_V6ONLYの設定に合わせて、より正確に検出できるようになりました。これは、特にデュアルスタック環境におけるネットワーク挙動の信頼性を向上させます。

関連リンク

参考にした情報源リンク