[インデックス 16779] ファイルの概要
このコミットは、Go言語のネットワークパッケージにおけるOpenBSDプラットフォームでのsetsockoptシステムコールに関するバグ修正です。具体的には、TCPキープアライブ設定に使用されるソケットオプション定数が、OpenBSD環境で正しくないためにビルドが失敗する問題を解決します。TCP_KEEPALIVEをSO_KEEPALIVEに修正することで、OpenBSD上でのGoのネットワーク機能の安定性と互換性を確保しています。
コミット
commit 9bd32b48ca135ea21a2ee634e849cd72d780ccef
Author: Joel Sing <jsing@google.com>
Date: Tue Jul 16 08:35:06 2013 -0700
net: fix setsockopt for openbsd
s/TCP_KEEPALIVE/SO_KEEPALIVE/ to unbreak build on OpenBSD.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/11345044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9bd32b48ca135ea21a2ee634e849cd72d780ccef
元コミット内容
net: fix setsockopt for openbsd
s/TCP_KEEPALIVE/SO_KEEPALIVE/ to unbreak build on OpenBSD.
変更の背景
この変更の背景には、Go言語のネットワークパッケージがOpenBSDオペレーティングシステム上でビルドされる際に発生していた問題があります。Goのnetパッケージは、OS固有のシステムコールをラップして、クロスプラットフォームで一貫したネットワークAPIを提供しています。TCPキープアライブ機能の設定には、setsockoptシステムコールが使用されますが、このシステムコールに渡すソケットオプションの定数が、OpenBSDと他のUnix系システム(Linuxなど)で異なっていました。
具体的には、Goのコードベースでは当初、TCPキープアライブの設定にsyscall.TCP_KEEPALIVEという定数を使用していたと考えられます。しかし、OpenBSDのシステムヘッダファイルやカーネルAPIでは、TCPキープアライブの有効化にはSO_KEEPALIVEという汎用ソケットオプションが使用され、TCP_KEEPALIVEは存在しないか、異なる意味を持つ可能性がありました。この不一致が原因で、OpenBSD上でGoのネットワークパッケージをコンパイルしようとすると、未定義のシンボルエラーや不正な定数使用によるビルドエラーが発生していました。
このコミットは、OpenBSDでのビルドを「壊さないようにする (unbreak build)」ことを目的としており、OpenBSD固有のネットワークコードにおいて、正しいソケットオプション定数を使用するように修正することで、このビルド問題を解決しています。
前提知識の解説
ソケットとソケットオプション
ソケットは、ネットワーク通信のエンドポイントを表す抽象化された概念です。アプリケーションはソケットを通じてデータを送受信します。ソケットには、その動作を制御するための様々な「ソケットオプション」が設定できます。これらのオプションは、TCP/IPプロトコルの動作、バッファサイズ、タイムアウト、キープアライブなど、多岐にわたります。
setsockoptシステムコール
setsockoptは、ソケットのオプションを設定するために使用されるシステムコールです。そのプロトタイプは一般的に以下のようになります(C言語の例):
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
sockfd: オプションを設定するソケットのファイルディスクリプタ。level: オプションが適用されるプロトコルレベル。例えば、ソケットレベルのオプションにはSOL_SOCKET、TCPレベルのオプションにはIPPROTO_TCPが指定されます。optname: 設定するオプションの名前。例えば、SO_KEEPALIVEやTCP_NODELAYなど。optval: オプションの値へのポインタ。optlen:optvalのサイズ。
このコミットでは、syscall.SetsockoptIntが使用されており、これはGo言語がsetsockoptシステムコールをラップしたものです。SetsockoptIntは、optvalが整数値である場合に便利です。
TCPキープアライブ (TCP Keep-Alive)
TCPキープアライブは、アイドル状態のTCP接続がまだ有効であるかどうかを確認するためのメカニズムです。これは、ネットワークの途中で接続が切断されたり、ピアがクラッシュしたりした場合に、アプリケーションがその状態を検知できるようにするために使用されます。
キープアライブが有効になっていると、一定期間データが交換されない場合に、TCPスタックは小さなキープアライブプローブパケットをピアに送信します。
- ピアから応答があれば、接続はまだ有効であると判断されます。
- ピアから応答がない場合、またはRSTパケットが返された場合、接続は切断されたと判断され、ソケットエラーが報告されます。
キープアライブの目的は、リソースの無駄遣いを防ぎ、アプリケーションが切断された接続に対して無限に待機するのを避けることです。
SO_KEEPALIVEとTCP_KEEPALIVE
SO_KEEPALIVE: これはソケットレベルのオプション(levelにSOL_SOCKETを指定)で、TCPキープアライブ機能を有効にするか無効にするかを制御します。このオプションを有効にすると、そのソケットに対するTCPキープアライブメカニズムが動作を開始します。多くのUnix系システムで共通して使用される汎用的なオプションです。TCP_KEEPALIVE: これはTCPプロトコルレベルのオプション(levelにIPPROTO_TCPを指定)で、キープアライブプローブの送信間隔や再試行回数など、キープアライブの具体的な動作パラメータを設定するために使用されることがあります。しかし、このオプションの存在や具体的な意味は、オペレーティングシステムの実装によって異なります。一部のシステムではTCP_KEEPALIVEが存在せず、キープアライブのパラメータは他の方法(例えば、システム全体のカーネルパラメータ)で設定されるか、SO_KEEPALIVEを有効にした上でデフォルト値が使用されます。
このコミットの核心は、OpenBSDではTCP_KEEPALIVEが期待通りに機能しないか、あるいは存在しないため、代わりにSO_KEEPALIVEを使用する必要があったという点です。SO_KEEPALIVEはキープアライブを有効にするためのより標準的で移植性の高い方法です。
Go言語のnetパッケージとsyscallパッケージ
netパッケージ: Go言語の標準ライブラリの一部で、ネットワークI/O機能を提供します。TCP/UDPソケット、IPアドレスの解決、HTTPクライアント/サーバーなどが含まれます。このパッケージは、OS固有のネットワークシステムコールを抽象化し、Goプログラムが異なるプラットフォームで同じコードでネットワーク通信を行えるようにします。syscallパッケージ: Go言語の標準ライブラリの一部で、低レベルのOSプリミティブ(システムコール)へのアクセスを提供します。netパッケージのような高レベルのパッケージは、内部的にsyscallパッケージを使用してOS固有の操作を実行します。syscall.SetsockoptIntはこのパッケージに含まれる関数で、setsockoptシステムコールをGoから呼び出すためのラッパーです。netFD: Goのnetパッケージ内部で使用される構造体で、ネットワークファイルディスクリプタ(fd)を抽象化したものです。sysfdフィールドは、基盤となるOSのファイルディスクリプタ(整数値)を保持しています。
技術的詳細
このコミットは、src/pkg/net/tcpsockopt_openbsd.goファイル内のsetKeepAlivePeriod関数に焦点を当てています。この関数は、TCPキープアライブの期間を設定するために使用されます。
元のコードでは、syscall.SetsockoptIntを呼び出す際に、levelとしてsyscall.IPPROTO_TCP(TCPプロトコルレベル)を指定し、optnameとしてsyscall.TCP_KEEPALIVEを使用していました。
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs))
しかし、OpenBSDのネットワークスタックの実装では、TCPキープアライブを有効にするための適切なソケットオプションはSO_KEEPALIVEであり、これはソケットレベルのオプション(levelはsyscall.SOL_SOCKET)として扱われるべきものです。TCP_KEEPALIVEという定数がOpenBSDのsetsockoptで期待される動作をしないか、あるいは定義されていないため、ビルドエラーが発生していました。
このコミットでは、optnameをsyscall.TCP_KEEPALIVEからsyscall.SO_KEEPALIVEに変更しています。
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.SO_KEEPALIVE, secs))
ここで注目すべきは、levelがsyscall.IPPROTO_TCPのままである点です。通常、SO_KEEPALIVEはSOL_SOCKETレベルのオプションとして設定されます。しかし、Goのnetパッケージの内部実装や、特定のOS(この場合はOpenBSD)のsetsockoptの挙動によっては、IPPROTO_TCPレベルでSO_KEEPALIVEを設定することが許容されるか、あるいはそのように実装されている可能性があります。あるいは、この特定のsetKeepAlivePeriod関数が、キープアライブの有効化だけでなく、その期間設定も同時に行おうとしており、その際にIPPROTO_TCPレベルのオプションとしてSO_KEEPALIVEが使用されるという、OpenBSD特有の挙動を反映している可能性も考えられます。
この変更により、OpenBSDのシステムコールインターフェースとGoのコードが整合し、TCPキープアライブ機能がOpenBSD上で正しく設定できるようになり、ビルドが成功するようになりました。os.NewSyscallErrorは、システムコールが失敗した場合に、エラーメッセージと元のエラーをラップしたerrorを生成するためのGoのユーティリティ関数です。
コアとなるコードの変更箇所
--- a/src/pkg/net/tcpsockopt_openbsd.go
+++ b/src/pkg/net/tcpsockopt_openbsd.go
@@ -23,5 +23,5 @@ func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
d += (time.Second - time.Nanosecond)
secs := int(d.Seconds())
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs))
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.SO_KEEPALIVE, secs))
}
コアとなるコードの解説
変更されたのは、src/pkg/net/tcpsockopt_openbsd.goファイル内のsetKeepAlivePeriod関数の一行です。
元のコード:
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs))
修正後のコード:
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.SO_KEEPALIVE, secs))
この変更は非常にシンプルですが、その影響は大きいです。
fd.sysfd: これは、TCPソケットに対応するOSのファイルディスクリプタです。setsockoptシステムコールはこのディスクリプタに対して操作を行います。syscall.IPPROTO_TCP: これは、設定するオプションがTCPプロトコルレベルのものであることを示します。syscall.TCP_KEEPALIVEからsyscall.SO_KEEPALIVEへの変更:TCP_KEEPALIVE: 元々使用されていた定数で、おそらく他のUnix系システム(Linuxなど)ではTCPキープアライブのパラメータ設定に用いられることを意図していたか、あるいはキープアライブの有効化にも使われていた可能性があります。しかし、OpenBSDではこの定数が適切に機能しないか、存在しないため、ビルドエラーの原因となっていました。SO_KEEPALIVE: 修正後に使用された定数です。これは、ソケットレベルでキープアライブ機能を有効にするための標準的なオプションです。OpenBSDでは、TCPキープアライブを有効にするためにこのSO_KEEPALIVEを使用することが正しい挙動となります。
secs: これは、キープアライブの期間(秒単位)を表す整数値です。setsockoptのoptval引数として渡されます。
この修正により、OpenBSD環境でGoのネットワークパッケージがTCPキープアライブ機能を設定しようとした際に、OSが期待する正しいソケットオプション定数SO_KEEPALIVEが使用されるようになり、ビルドエラーが解消されました。これにより、OpenBSD上でのGoアプリケーションがTCPキープアライブを正しく利用できるようになります。
関連リンク
参考にした情報源リンク
- setsockopt(2) - Linux man page
- socket(7) - Linux man page (SO_KEEPALIVE)
- tcp(7) - Linux man page (TCP_KEEPALIVE)
- OpenBSD man pages (setsockopt) (具体的なOpenBSDのmanページはバージョンによって異なるため、一般的な参照として)
- Go言語の
netパッケージドキュメント - Go言語の
syscallパッケージドキュメント - Go言語の
osパッケージドキュメント - TCP Keepalive HOWTO (TCPキープアライブの一般的な概念について)