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

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

src/pkg/net/packetconn_test.go は、Go言語の標準ライブラリである net パッケージにおける PacketConn インターフェースのテストコードです。PacketConn は、UDPやIPなどのパケット指向のネットワーク接続を表すインターフェースであり、このファイルではその実装が正しく機能するかどうかを検証するための様々なテストケースが含まれています。特に、異なるネットワークタイプ(UDP、IP、Unixgram)やオペレーティングシステム(Windows、Plan 9など)における挙動がテストされています。

コミット

net: enable PacketConn test for raw IP network on Windows

Just forgot to include this in CL 12843043.
Also consolidates the code dealing with test environment.

Update #6122

R=alex.brainman
CC=golang-dev
https://golang.org/cl/13184043

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

https://github.com/golang/go/commit/519a9e8e9bdfa9b8a1b5c0c1c5be7a16718f7992

元コミット内容

net: enable PacketConn test for raw IP network on Windows

Just forgot to include this in CL 12843043.
Also consolidates the code dealing with test environment.

Update #6122

R=alex.brainman
CC=golang-dev
https://golang.org/cl/13184043

変更の背景

このコミットは、主に以下の2つの目的のために行われました。

  1. Windows上でのRaw IPネットワークテストの有効化: 以前の変更(CL 12843043)でWindows環境におけるRaw IPネットワークのサポートが追加されたにもかかわらず、関連するテストが有効になっていませんでした。このコミットは、そのテストを有効にすることで、Windows環境でのRaw IP機能の動作保証を強化します。
  2. テスト環境設定コードの統合: テストコード内で、異なるネットワークタイプ(UDP、IP、Unixgram)やオペレーティングシステム(Windows、Plan 9など)に応じたテストデータの準備やスキップロジックが重複して記述されていました。このコミットでは、これらの共通ロジックを packetConnTestData というヘルパー関数に集約し、コードの重複を排除し、可読性と保守性を向上させています。

また、コミットメッセージにある Update #6122 は、GoのIssueトラッカーにおけるIssue 6122に関連する変更であることを示唆しています。Issue 6122は「net: raw IP support for Windows」というタイトルで、WindowsにおけるRaw IPソケットのサポートに関する議論と実装の進捗を追跡するためのものでした。このコミットは、そのIssueの解決に向けた最終的なテストの有効化とコードの整理を目的としています。

前提知識の解説

PacketConn インターフェース

net.PacketConn はGo言語の net パッケージで定義されているインターフェースで、パケット指向のネットワーク接続(例: UDP、IP)を抽象化します。主なメソッドは以下の通りです。

  • ReadFrom(b []byte) (n int, addr Addr, err error): パケットを読み込み、送信元アドレスを返します。
  • WriteTo(b []byte, addr Addr) (n int, err error): 指定されたアドレスにパケットを書き込みます。
  • LocalAddr() Addr: ローカルアドレスを返します。
  • SetDeadline(t time.Time) error: 読み書きのデッドラインを設定します。

Raw IPネットワーク

Raw IPソケットは、トランスポート層(TCPやUDP)を介さずに、直接IP層のパケットを送受信するためのソケットです。これにより、アプリケーションはIPヘッダやペイロードを自由に構築・解析できます。ICMP(Internet Control Message Protocol)パケットの送受信や、カスタムプロトコルの実装などに利用されます。

Raw IPソケットの利用には、通常、管理者権限(root権限)が必要です。これは、Raw IPソケットがネットワークスタックの低レベルな部分にアクセスするため、悪用されるとシステムに深刻な影響を与える可能性があるためです。

ICMP (Internet Control Message Protocol)

ICMPは、IPネットワーク上でエラーメッセージや運用情報を交換するために使用されるプロトコルです。例えば、ping コマンドはICMP Echo Request/Replyメッセージを使用して、ホスト間の到達可能性をテストします。このコミットのテストコードでは、ICMP Echo Requestパケットを構築してRaw IPソケットで送信し、その応答を検証しています。

  • icmpv4EchoRequest: IPv4におけるICMP Echo Requestメッセージのタイプコード。
  • icmpMessage: ICMPメッセージの構造体。
  • icmpEcho: ICMP Echoメッセージのボディ部分の構造体。IDとシーケンス番号、データを含みます。
  • Marshal(): ICMPメッセージ構造体をバイト列に変換するメソッド。

os.Getuid()

os.Getuid() は、現在のプロセスの実効ユーザーID(UID)を返します。Unix系システムでは、UIDが0の場合、そのプロセスはroot権限で実行されていることを意味します。Raw IPソケットのテストでは、root権限が必要な場合があるため、この関数で権限を確認しています。

runtime.GOOS

runtime.GOOS は、Goプログラムがコンパイルされ、実行されているオペレーティングシステムの名前(例: "linux", "windows", "darwin", "plan9")を文字列で返します。このコミットでは、特定のOS(Plan 9やWindows)でのみテストをスキップする、あるいは特定のOSでのみテストを有効にするために使用されています。

Goのテストフレームワーク (testing.T)

Go言語には標準でテストフレームワークが組み込まれており、testing パッケージを使用します。

  • func TestXxx(t *testing.T): テスト関数は Test で始まり、*testing.T 型の引数を取ります。
  • t.Logf(...): テスト中にログメッセージを出力します。
  • t.Fatalf(...): テストを失敗させ、メッセージを出力してテスト関数を終了します。
  • continue: テストループ内で continue を使用すると、現在のイテレーションをスキップし、次のイテレーションに進みます。これは、特定の条件(例: OSがサポートされていない、root権限がない)でテストケースをスキップする際に利用されます。

技術的詳細

このコミットの主要な変更点は、packetConnTestData という新しいヘルパー関数の導入と、既存のテスト関数 TestPacketConn および TestConnAndPacketConn からの重複コードの削除です。

packetConnTestData 関数の導入

packetConnTestData 関数は、与えられたネットワークタイプ(net)とテストインデックス(i)に基づいて、テストで使用するバイトデータと、テストをスキップまたは致命的なエラーとして終了するための関数を返します。

  • udp ケース: 単純なUDPテストデータを返します。
  • ip ケース:
    • plan9 の場合はテストをスキップする関数を返します。
    • windows の場合は、以前はスキップされていたRaw IPテストが、このコミットによって有効化され、ICMP Echo Requestパケットを構築して返します。
    • その他のOSでは、os.Getuid() != 0 (root権限がない場合)にテストをスキップする関数を返します。
    • ICMP Echo Requestパケットの構築には、icmpMessageicmpEcho 構造体が使用され、Marshal() メソッドでバイト列に変換されます。
  • unixgram ケース:
    • plan9 または windows の場合はテストをスキップする関数を返します。
    • その他のOSでは、Unixgramテストデータを返します。
  • default ケース: 未知のネットワークタイプの場合にテストをスキップする関数を返します。

この関数は、テストデータの準備と、特定の環境でのテストのスキップロジックを一元化することで、テストコードの重複を大幅に削減し、保守性を向上させています。

テスト関数の変更

TestPacketConn および TestConnAndPacketConn 関数では、以前はネットワークタイプごとに異なる switch 文でテストデータの準備とスキップロジックが直接記述されていました。このコミットにより、これらの switch 文が削除され、代わりに packetConnTestData 関数が呼び出されるようになりました。

// 変更前 (抜粋)
switch netstr[0] {
case "udp":
    wb = []byte("UDP PACKETCONN TEST")
case "ip":
    switch runtime.GOOS {
    case "plan9":
        continue
    }
    if os.Getuid() != 0 {
        continue
    }
    // ... ICMPパケット構築ロジック ...
case "unixgram":
    // ...
default:
    continue
}

// 変更後 (抜粋)
wb, skipOrFatalFn := packetConnTestData(t, netstr[0], i)
if skipOrFatalFn != nil {
    skipOrFatalFn()
    continue
}

この変更により、テストコードの本体はより簡潔になり、テストデータの準備や環境依存のスキップロジックの詳細から分離されました。

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

変更は src/pkg/net/packetconn_test.go ファイルに集中しています。

具体的には、以下の部分がコアとなる変更箇所です。

  1. packetConnTestData 関数の新規追加:
    func packetConnTestData(t *testing.T, net string, i int) ([]byte, func()) {
        // ... 実装 ...
    }
    
  2. TestPacketConn 関数内の変更: wb の初期化と、ネットワークタイプに応じた switch 文が削除され、packetConnTestData の呼び出しに置き換えられました。
    --- a/src/pkg/net/packetconn_test.go
    +++ b/src/pkg/net/packetconn_test.go
    @@ -42,37 +89,10 @@ func TestPacketConn(t *testing.T) {
      }
    
      for i, tt := range packetConnTests {
    -		var wb []byte
      		netstr := strings.Split(tt.net, ":")
    -		switch netstr[0] {
    -		case "udp":
    -			wb = []byte("UDP PACKETCONN TEST")
    -		case "ip":
    -			switch runtime.GOOS {
    -			case "plan9":
    -				continue
    -			}
    -			if os.Getuid() != 0 {
    -				continue
    -			}
    -			var err error
    -			wb, err = (&icmpMessage{
    -				Type: icmpv4EchoRequest, Code: 0,
    -				Body: &icmpEcho{
    -					ID: os.Getpid() & 0xffff, Seq: i + 1,
    -					Data: []byte("IP PACKETCONN TEST"),
    -				},
    -			}).Marshal()
    -			if err != nil {
    -				t.Fatalf("icmpMessage.Marshal failed: %v", err)
    -			}
    -		case "unixgram":
    -			switch runtime.GOOS {
    -			case "plan9", "windows":
    -				continue
    -			}
    -			wb = []byte("UNIXGRAM PACKETCONN TEST")
    -		default:
    +		wb, skipOrFatalFn := packetConnTestData(t, netstr[0], i)
    +		if skipOrFatalFn != nil {
    +			skipOrFatalFn()
      			continue
      		}
    
  3. TestConnAndPacketConn 関数内の変更: TestPacketConn と同様に、重複するテストデータ準備とスキップロジックが packetConnTestData の呼び出しに置き換えられました。
    --- a/src/pkg/net/packetconn_test.go
    +++ b/src/pkg/net/packetconn_test.go
    @@ -127,35 +147,9 @@ func TestConnAndPacketConn(t *testing.T) {
      }
    
      for i, tt := range packetConnTests {
    -		var wb []byte
      		netstr := strings.Split(tt.net, ":")
    -		switch netstr[0] {
    -		case "udp":
    -			wb = []byte("UDP PACKETCONN TEST")
    -		case "ip":
    -			switch runtime.GOOS {
    -			case "plan9":
    -				continue
    -			}
    -			if os.Getuid() != 0 {
    -				continue
    -			}
    -			var err error
    -			wb, err = (&icmpMessage{
    -				Type: icmpv4EchoRequest, Code: 0,
    -				Body: &icmpEcho{
    -					ID: os.Getpid() & 0xffff, Seq: i + 1,
    -					Data: []byte("IP PACKETCONN TEST"),
    -				},
    -			}).Marshal()
    -			if err != nil {
    -				t.Fatalf("icmpMessage.Marshal failed: %v", err)
    -			}
    -		case "unixgram":
    -			switch runtime.GOOS {
    -			case "plan9", "windows":
    -				continue
    -			}
    -			wb = []byte("UNIXGRAM PACKETCONN TEST")
    -		default:
    +		wb, skipOrFatalFn := packetConnTestData(t, netstr[0], i)
    +		if skipOrFatalFn != nil {
    +			skipOrFatalFn()
      			continue
      		}
    

コアとなるコードの解説

このコミットの核心は、テストコードのDRY (Don't Repeat Yourself) 原則への準拠と、Windows環境でのRaw IPテストの有効化です。

新しく導入された packetConnTestData 関数は、テストのセットアップロジックをカプセル化しています。この関数は、テスト対象のネットワークタイプ(udp, ip, unixgram)に基づいて、適切なテストデータ(バイトスライス)と、テストをスキップすべきか、あるいは致命的なエラーとして扱うべきかを判断する関数(func())を返します。

特に注目すべきは、ip ネットワークタイプの場合の処理です。

	case "ip":
		switch runtime.GOOS {
		case "plan9":
			return nil, func() {
				t.Logf("skipping %q test on %q", net, runtime.GOOS)
			}
		case "windows": // ここでWindowsでのRaw IPテストが有効化される
		default:
			if os.Getuid() != 0 {
				return nil, func() {
					t.Logf("skipping %q test; must be root", net)
				}
			}
		}
		// ICMP Echo Requestパケットの構築
		b, err := (&icmpMessage{
			Type: icmpv4EchoRequest, Code: 0,
			Body: &icmpEcho{
				ID: os.Getpid() & 0xffff, Seq: i + 1,
				Data: []byte("IP PACKETCONN TEST"),
			},
		}).Marshal()
		if err != nil {
			return nil, func() {
				t.Fatalf("icmpMessage.Marshal failed: %v", err)
			}
		}
		return b, nil

以前のコードでは、windows のケースで continue があり、テストがスキップされていました。このコミットでは、windowscase ブロックが空になり、その後のICMPパケット構築ロジックが実行されるようになりました。これにより、Windows上でもRaw IPネットワークの PacketConn テストが実行されるようになります。

また、os.Getuid() != 0 のチェックは、Unix系システムでRaw IPソケットを使用するためにroot権限が必要な場合にテストをスキップするためのものです。Windowsではこのチェックは不要なため、windowscase の後に配置されています。

TestPacketConnTestConnAndPacketConn の両テスト関数では、この packetConnTestData 関数を呼び出すことで、テストデータの準備と環境依存のスキップロジックを共通化しています。これにより、コードの重複が解消され、テストコード全体の保守性と可読性が向上しました。もし将来的に新しいOSやネットワークタイプが追加された場合でも、packetConnTestData 関数を修正するだけで、既存のテスト関数に影響を与えることなく対応できるようになります。

関連リンク

参考にした情報源リンク