[インデックス 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_V6ONLY
を1
(true)に設定した場合:- そのIPv6ソケットはIPv6パケットのみを処理します。
- IPv4-mapped IPv6アドレスを介したIPv4通信は受け付けません。
- これにより、IPv4アプリケーションとIPv6アプリケーションが同じポートに同時にバインドできる可能性があります(ただし、これはOSの実装に依存します)。
- Goの
net
パッケージでは、IPv6専用の通信を行う場合にこの設定を使用します。
-
IPV6_V6ONLY
を0
(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
コアとなるコードの解説
-
probes
構造体へのvalue
フィールドの追加:var probes = []struct { laddr TCPAddr value int // <-- 追加されたフィールド ok bool }{
probes
スライス内の匿名構造体にvalue
というint
型のフィールドが追加されました。このフィールドは、各プローブ(テストケース)でIPV6_V6ONLY
ソケットオプションに設定する値を保持するために使用されます。 -
IPv6通信能力テストの
value
設定:// IPv6 communication capability -{TCPAddr{IP: ParseIP("::1")}, false}, +{laddr: TCPAddr{IP: ParseIP("::1")}, value: 1}, // <-- value: 1 に変更
IPv6ループバックアドレス
::1
を用いたIPv6通信能力のテストケースにおいて、value
が1
に設定されました。これは、このテストソケットがIPV6_V6ONLY=1
(IPv6専用)として動作することを意味します。これにより、純粋なIPv6通信がシステムで適切に機能するかどうかが、GoがIPv6専用ソケットを使用する際と同じ条件でテストされます。 -
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アドレス通信能力のテストケースにおいて、value
が0
に設定されました。これは、このテストソケットがIPV6_V6ONLY=0
(デュアルスタックモード)として動作することを意味します。これにより、IPv4-mappedアドレスを介したIPv4通信がシステムで適切に機能するかどうかが、Goがデュアルスタックソケットを使用する際と同じ条件でテストされます。 -
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
の設定に合わせて、より正確に検出できるようになりました。これは、特にデュアルスタック環境におけるネットワーク挙動の信頼性を向上させます。
関連リンク
- Go
net
パッケージのドキュメント: https://pkg.go.dev/net syscall
パッケージのドキュメント: https://pkg.go.dev/syscallIPV6_V6ONLY
に関するRFC (RFC 3493 - Basic Socket Interface Extensions for IPv6): https://datatracker.ietf.org/doc/html/rfc3493#section-5.3
参考にした情報源リンク
IPV6_V6ONLY
socket option: https://man7.org/linux/man_pages/man7/ipv6.7.htmlIPV6_V6ONLY
on Windows: https://learn.microsoft.com/en-us/windows/win32/winsock/ipv6-only-sockets- Stack Overflow discussions on
IPV6_V6ONLY
: - Go CL 80140044: https://go-review.googlesource.com/c/go/+/80140044 (これは元コミット内容に記載されているリンクと同じですが、詳細なレビューコメントが含まれている可能性があります)