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

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

このコミットは、Go言語のネットワークパッケージにおけるWindows環境でのRawソケットテストの実行に関する変更を扱っています。具体的には、以前の変更によって無効化されていたRawソケットテストを、ユーザーがRawソケットを作成する権限を持っている場合にのみ実行するように再有効化するものです。

コミット

commit b80ef1ab48b9829e8b974effadc0740d6645cc39
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Tue Sep 24 13:15:49 2013 +1000

    net: re-enable raw socket tests on windows
    
    Since CL 38bf89161a72 raw socket tests are not executed
    on windows builders. This change re-enable them again.
    It will attempt to run raw socket tests only if user
    is permitted to create raw socket by OS.
    
    Fixes #6392
    
    R=golang-dev
    CC=golang-dev, mikioh.mikioh, rsc
    https://golang.org/cl/13422044

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

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

元コミット内容

net: re-enable raw socket tests on windows

Since CL 38bf89161a72 raw socket tests are not executed
on windows builders. This change re-enable them again.
It will attempt to run raw socket tests only if user
is permitted to create raw socket by OS.

Fixes #6392

R=golang-dev
CC=golang-dev, mikioh.mikioh, rsc
https://golang.org/cl/13422044

変更の背景

このコミットの背景には、Go言語のネットワークパッケージにおけるRawソケット関連のテストが、特定の変更(CL 38bf89161a72)以降、Windowsビルド環境で実行されなくなっていたという問題があります。Rawソケットは、通常のソケット通信ではアクセスできない低レベルのネットワークプロトコル(例: ICMP)に直接アクセスするために使用されます。しかし、Rawソケットの作成には通常、オペレーティングシステム(OS)レベルでの特別な権限が必要です。

Windows環境では、Rawソケットの作成には管理者権限が要求されます。以前の変更により、Windowsビルド環境でこれらのテストがスキップされるようになっていましたが、これはテストカバレッジの低下を意味します。このコミットの目的は、Windows上でのRawソケットテストを再度有効にすることです。ただし、無条件に有効にするのではなく、テストを実行するユーザーがOSによってRawソケットを作成する権限を持っている場合にのみ実行するように変更することで、テストの安定性と信頼性を確保しています。これにより、権限のない環境でのテスト失敗を防ぎつつ、Rawソケット機能のテストを継続できるようになります。

前提知識の解説

Rawソケット

Rawソケットは、TCPやUDPのような上位プロトコルを介さずに、IP層やそれ以下のデータリンク層のパケットに直接アクセスするためのソケットです。これにより、開発者はカスタムプロトコルを実装したり、ネットワークトラフィックを詳細に分析したり、ICMP(Internet Control Message Protocol)のようなプロトコルを直接操作したりすることができます。

Rawソケットは非常に強力であるため、悪用される可能性もあります(例: パケットの偽装、ネットワークスキャン)。このため、ほとんどのオペレーティングシステムでは、Rawソケットの作成や使用に特別な権限(通常は管理者権限やroot権限)を要求します。

WindowsにおけるRawソケットの権限

Windowsでは、SOCK_RAWタイプのソケットを使用するには管理者権限が必要です。Microsoftのドキュメント(MSDN)によると、Windows Vista以降ではソケット作成時にアクセス権が強制され、それ以前のバージョンでは他のソケット操作中に強制されます。権限がない場合、WSAEACCES(Access Denied)エラーが発生します。

Go言語の net パッケージとテストフレームワーク

Go言語の標準ライブラリには、ネットワーク通信を扱うための net パッケージが含まれています。このパッケージは、TCP/UDPソケットだけでなく、IPソケットやRawソケットのような低レベルのネットワーク機能もサポートしています。

Goのテストフレームワークは、testing パッケージによって提供されます。テスト関数は Test で始まる名前を持ち、*testing.T 型の引数を取ります。テスト内で t.Skip() を呼び出すことで、特定の条件が満たされない場合にテストをスキップすることができます。これは、特定のOSや環境でのみ実行可能なテスト、または特定の権限が必要なテストでよく使用されます。

syscall パッケージと os.Getuid()

Go言語の syscall パッケージは、低レベルのOSプリミティブへのアクセスを提供します。これには、ソケットの作成(syscall.Socket)やクローズ(syscall.Closesocket)などの関数が含まれます。

os.Getuid() 関数は、現在のプロセスの実効ユーザーID(UID)を返します。Unix系システムでは、UIDが0であることは通常、プロセスがroot権限で実行されていることを意味します。WindowsにはUIDの概念はありませんが、このコミットではWindows固有の権限チェックメカニズムが導入されています。

技術的詳細

このコミットの主要な技術的変更点は、Rawソケットテストをスキップするかどうかを判断するための新しいヘルパー関数 skipRawSocketTests() の導入と、既存のテストコードにおけるOSごとの条件分岐の整理です。

skipRawSocketTests() 関数の導入

各OS(Plan 9, Unix, Windows)の net パッケージの内部ファイル(fd_plan9.go, fd_unix.go, fd_windows.go)に、skipRawSocketTests() という関数が追加されました。この関数は、Rawソケットテストをスキップすべきかどうか、スキップする場合の理由メッセージ、および発生したエラーを返します。

  • src/pkg/net/fd_plan9.go: Plan 9ではRawソケットテストを常にスキップするように設定されています。

    func skipRawSocketTests() (skip bool, skipmsg string, err error) {
    	return true, "skipping test on plan9", nil
    }
    
  • src/pkg/net/fd_unix.go: Unix系システムでは、os.Getuid() != 0(root権限でない場合)にテストをスキップします。

    func skipRawSocketTests() (skip bool, skipmsg string, err error) {
    	if os.Getuid() != 0 {
    		return true, "skipping test; must be root", nil
    	}
    	return false, "", nil
    }
    
  • src/pkg/net/fd_windows.go: Windowsでは、実際にRawソケットを作成してみて、syscall.WSAEACCES エラーが発生するかどうかで権限をチェックします。エラーが発生した場合はスキップし、それ以外のエラーが発生した場合はそのエラーを返します。

    func skipRawSocketTests() (skip bool, skipmsg string, err error) {
    	// From http://msdn.microsoft.com/en-us/library/windows/desktop/ms740548.aspx:
    	// Note: To use a socket of type SOCK_RAW requires administrative privileges.
    	// Users running Winsock applications that use raw sockets must be a member of
    	// the Administrators group on the local computer, otherwise raw socket calls
    	// will fail with an error code of WSAEACCES. On Windows Vista and later, access
    	// for raw sockets is enforced at socket creation. In earlier versions of Windows,
    	// access for raw sockets is enforced during other socket operations.
    	s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, 0)
    	if err == syscall.WSAEACCES {
    		return true, "skipping test; no access to raw socket allowed", nil
    	}
    	if err != nil {
    		return true, "", err
    	}
    	defer syscall.Closesocket(s)
    	return false, "", nil
    }
    

    このWindows固有の実装は、Rawソケットの作成が成功するかどうかを試すことで、実行時の権限を動的にチェックするという賢明なアプローチです。

テストコードの変更

ipraw_test.go, packetconn_test.go, protoconn_test.go など、Rawソケットを使用するテストファイルでは、既存の switch runtime.GOOS によるOSごとの条件分岐が削除され、新しく導入された skipRawSocketTest(t *testing.T) ヘルパー関数が使用されるようになりました。

skipRawSocketTest 関数は、skipRawSocketTests() を呼び出し、エラーが発生した場合は t.Fatal() でテストを終了させ、スキップすべき場合は t.Skip() でテストをスキップします。

例: src/pkg/net/ipraw_test.go の変更

--- a/src/pkg/net/ipraw_test.go
+++ b/src/pkg/net/ipraw_test.go
@@ -59,6 +59,14 @@ func init() {
 	}
 }
 
+func skipRawSocketTest(t *testing.T) (skip bool, skipmsg string) {
+	skip, skipmsg, err := skipRawSocketTests()
+	if err != nil {
+		t.Fatal(err)
+	}
+	return skip, skipmsg
+}
+
 func TestResolveIPAddr(t *testing.T) {
 	for _, tt := range resolveIPAddrTests {
 		addr, err := ResolveIPAddr(tt.net, tt.litAddrOrName)
@@ -80,17 +88,8 @@ var icmpEchoTests = []struct {
 }
 
 func TestConnICMPEcho(t *testing.T) {
-	switch runtime.GOOS {
-	case "plan9":
-		t.Skipf("skipping test on %q", runtime.GOOS)
-	case "windows":
-		if testing.Short() || !*testExternal {
-			t.Skipf("skipping test on %q to avoid network firewall", runtime.GOOS)
-		}
-	default:
-		if os.Getuid() != 0 {
-			t.Skip("skipping test; must be root")
-		}
+	if skip, skipmsg := skipRawSocketTest(t); skip {
+		t.Skip(skipmsg)
 	}
 
 	for i, tt := range icmpEchoTests {

この変更により、テストコードがより簡潔になり、OSごとのRawソケット権限チェックロジックが一元化されました。

syscall/ztypes_windows.go の変更

Windows固有のエラーコード WSAEACCESsrc/pkg/syscall/ztypes_windows.go に追加されました。これは、WindowsのRawソケット権限チェックで使用されるエラーコードをGoの syscall.Errno 型で認識できるようにするためです。

--- a/src/pkg/syscall/ztypes_windows.go
+++ b/src/pkg/syscall/ztypes_windows.go
@@ -22,6 +22,7 @@ const (
 	ERROR_OPERATION_ABORTED   Errno = 995
 	ERROR_IO_PENDING          Errno = 997
 	ERROR_NOT_FOUND           Errno = 1168
+	WSAEACCES                 Errno = 10013
 )
 
 const (

WSAEACCES はWinsock APIでアクセス拒否を示すエラーコードであり、値は 10013 です。

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

  • src/pkg/net/fd_plan9.go: skipRawSocketTests() 関数の追加
  • src/pkg/net/fd_unix.go: skipRawSocketTests() 関数の追加
  • src/pkg/net/fd_windows.go: skipRawSocketTests() 関数の追加
  • src/pkg/net/ipraw_test.go: skipRawSocketTest() ヘルパー関数の追加と、既存テストでの利用
  • src/pkg/net/multicast_test.go: Windowsでのテストスキップ条件の削除
  • src/pkg/net/packetconn_test.go: packetConnTestData 関数内でのテストスキップ条件の変更
  • src/pkg/net/protoconn_test.go: TestIPConnSpecificMethods でのテストスキップ条件の変更
  • src/pkg/syscall/ztypes_windows.go: WSAEACCES エラーコードの追加

コアとなるコードの解説

このコミットの核心は、Rawソケットテストの実行を、OSがRawソケットの作成を許可しているかどうかに基づいて動的に判断するメカニズムを導入した点です。

  1. skipRawSocketTests() の導入: この関数は、各OSの特性に合わせてRawソケットの作成権限をチェックします。

    • Plan 9ではRawソケットがサポートされていないため、常にスキップします。
    • Unix系では、os.Getuid() を使ってroot権限があるかを確認します。
    • Windowsでは、実際に syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, 0) を呼び出してRawソケットの作成を試みます。この試行が WSAEACCES エラー(アクセス拒否)を返した場合、ユーザーにRawソケットを作成する権限がないと判断し、テストをスキップします。これにより、Windows環境でのテスト実行時に、権限不足による不必要な失敗を防ぎつつ、権限がある場合にはテストが実行されるようになります。
  2. テストコードの簡素化: ipraw_test.go などのテストファイルでは、以前は runtime.GOOS を使った冗長なOSごとの条件分岐がありました。このコミットでは、skipRawSocketTest(t *testing.T) という新しいヘルパー関数を導入し、この関数内で skipRawSocketTests() を呼び出すようにしました。これにより、テストコードはよりクリーンになり、OSごとの権限チェックロジックが skipRawSocketTests() にカプセル化されました。テスト関数は単に if skip, skipmsg := skipRawSocketTest(t); skip { t.Skip(skipmsg) } のパターンに従うだけでよくなりました。

  3. WSAEACCES の定義: WindowsのRawソケット権限チェックで必要となる WSAEACCES エラーコードが syscall パッケージに追加されました。これにより、GoプログラムがWindows APIから返されるこの特定のエラーを適切に認識し、処理できるようになります。

これらの変更により、GoのネットワークパッケージにおけるRawソケットテストは、Windows環境においてもより堅牢かつ適切に実行されるようになりました。

関連リンク

  • GitHubコミットページ: https://github.com/golang/go/commit/b80ef1ab48b9829e8b974effadc0740d6645cc39
  • Go CL: https://golang.org/cl/13422044
  • CL 38bf89161a72 について: コミットメッセージで言及されている CL 38bf89161a72 は、Goの内部的な変更リストIDである可能性が高く、公開されているGoリポジトリのコミットハッシュとは直接対応しないようです。Web検索では、このハッシュに一致するGoのコミットは確認できませんでした。
  • Fixes #6392 について: Web検索では、Goの公式リポジトリにおける issue #6392 は確認できませんでした。このイシュー番号は、Goプロジェクトの外部のイシュートラッカーを参照している可能性があります。

参考にした情報源リンク