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

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

このコミットは、Go言語のnetパッケージにおいて、Windows環境でのRaw IPネットワークへのダイヤル(接続試行)に関する問題を修正し、Raw IPテストにおけるプラットフォーム依存のデータグラム切り捨て挙動を回避することを目的としています。特に、WindowsのConnectEx APIがコネクションレスソケットをサポートしないという制約に対処しています。

コミット

commit 180da80e9004cd6e1bbfc026890a12e86d0f9f5d
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Fri Aug 23 19:31:24 2013 +0900

    net: fix dial to raw IP networks on Windows
    
    Also avoids platform-dependent datagram truncation in raw IP tests.
    At least it's different between Windows and others.
    
    Fixes #6122.
    
    R=alex.brainman
    CC=golang-dev
    https://golang.org/cl/12843043

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

https://github.com/golang/go/commit/180da80e9004cd6e1bbfc026890a12e86d0f9f5d

元コミット内容

net: fix dial to raw IP networks on Windows

Also avoids platform-dependent datagram truncation in raw IP tests.
At least it's different between Windows and others.

Fixes #6122.

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

変更の背景

このコミットの主な背景には、Go言語のnetパッケージがWindows上でRaw IPネットワークに接続する際に直面していた問題があります。具体的には、以下の2点が挙げられます。

  1. WindowsのConnectEx APIの制約: WindowsのConnectEx APIは、TCPのようなコネクション指向ソケットのために設計されており、UDPやRaw IPのようなコネクションレスソケットでは使用できません。Goのネットワークスタックが内部的にConnectExを使用しようとすると、Raw IPネットワークへのダイヤルが失敗する可能性がありました。
  2. Raw IPテストにおけるデータグラムの切り捨て: Raw IPソケットのテストにおいて、プラットフォーム(特にWindowsとそれ以外のOS)間でデータグラムの切り捨て挙動が異なっていました。これにより、テストの信頼性が損なわれ、異なる環境でテスト結果が不安定になる問題がありました。

これらの問題は、Goのネットワーク機能のクロスプラットフォーム互換性と信頼性を確保する上で重要であり、このコミットによって修正が試みられました。コミットメッセージにあるFixes #6122は、この変更が特定のバグまたは課題を解決することを示唆しています。

前提知識の解説

このコミットを理解するためには、以下の技術的背景知識が役立ちます。

  • Raw IPソケット:
    • Raw IPソケットは、アプリケーションがIPヘッダを含む生(Raw)のIPパケットを直接送受信できるようにするソケットの一種です。通常のTCPやUDPソケットがトランスポート層(TCP/UDPヘッダ)までをOSが処理するのに対し、Raw IPソケットではアプリケーションがIP層以下のパケット構造を直接操作できます。
    • これにより、カスタムプロトコルの実装、ネットワーク診断ツール(例: pingtraceroute)、ファイアウォール、VPNなどの開発が可能になります。
    • セキュリティ上の理由から、Raw IPソケットの作成や使用には通常、管理者権限(Unix系OSではroot権限、Windowsでは管理者権限)が必要です。
  • ConnectEx API (Windows):
    • ConnectExは、Windows Winsock APIの一部であり、クライアントソケットがリモートエンドポイントへの接続を確立し、同時に最初のデータブロックを送信するための高性能な関数です。
    • このAPIは、主にTCPのようなコネクション指向プロトコル向けに最適化されており、接続確立とデータ送信を単一のシステムコールで効率的に行うことができます。
    • 重要な点として、ConnectExはコネクションレスソケット(UDPやRaw IP)では使用できません。これは、コネクションレスプロトコルには「接続」という概念がないためです。
  • syscall.Handle:
    • Go言語のsyscallパッケージは、オペレーティングシステムの低レベルなシステムコールへのアクセスを提供します。syscall.Handleは、Windows APIにおける汎用的なハンドル型を表します。ファイル、ソケット、イベントなど、様々なカーネルオブジェクトへの参照として使用されます。
  • os.Getuid():
    • osパッケージの一部で、現在のプロセスの実効ユーザーID(UID)を返します。Unix系OSにおいて、rootユーザー(UIDが0)であるかどうかを判断するためによく使用されます。Raw IPソケットの操作など、特権が必要な処理を行う前に権限を確認するために利用されます。
  • runtime.GOOS:
    • Go言語のruntimeパッケージの一部で、プログラムが実行されているオペレーティングシステムの名前(例: "linux", "windows", "darwin", "plan9"など)を文字列で返します。これにより、コード内でOS固有の挙動を条件分岐させることができます。
  • データグラムの切り捨て (Datagram Truncation):
    • データグラム(UDPやIPパケットなど)は、固定長ではなく可変長であることが一般的です。ネットワークスタックや受信バッファのサイズ制限により、受信したデータグラムが完全にバッファに収まらない場合、データの一部が切り捨てられることがあります。
    • この挙動はOSやネットワークデバイス、設定によって異なる場合があり、特にテスト環境では予期せぬ結果を引き起こす可能性があります。

技術的詳細

このコミットは、主に以下の技術的な問題に対処しています。

  1. WindowsにおけるConnectExの誤用回避:
    • Goのnetパッケージの内部実装において、ソケットの接続処理にConnectExが使用される場合があります。しかし、Raw IPソケットはコネクションレスであるため、ConnectExを使用するとエラーが発生します。
    • このコミットでは、fd_windows.go内のcanUseConnectEx関数が修正され、"ip", "ip4", "ip6"といったRaw IPネットワークタイプに対してもConnectExを使用しないように変更されました。これにより、Windows上でのRaw IPネットワークへのダイヤルが正しく行われるようになります。
  2. Raw IPテストにおけるプラットフォーム依存のデータグラムサイズ問題:
    • ipraw_test.go内のICMP Echoテスト(TestConnICMPEchoおよびTestPacketConnICMPEcho)では、ICMPメッセージの送受信が行われます。
    • 元のコードでは、受信バッファのサイズが送信データグラムのサイズに依存していました。しかし、Windows環境では、Raw IPソケットで受信されるデータグラムのサイズが他のOSと異なる場合があり、特にICMP Echo Replyのような応答パケットにおいて、元のリクエストパケットのデータ部分が切り捨てられる可能性がありました。
    • このコミットでは、受信バッファ(rb)のサイズを、送信データグラム(wb)のサイズにIPヘッダの最大長(20バイト)を加えたものとして確保するように変更されました。これにより、受信時にデータグラムが切り捨てられることなく、完全なパケットを受信できるようになり、テストの信頼性が向上しました。
  3. テストのOS依存性ハンドリングの改善:
    • Raw IPソケットのテストは、OSのセキュリティモデルやネットワークスタックの実装に大きく依存します。特に、Unix系OSではroot権限が必要であり、WindowsではRaw IPソケットの挙動が異なる場合があります。また、Plan 9のような一部のOSではRaw IPソケットがサポートされていないか、挙動が大きく異なります。
    • このコミットでは、ipraw_test.goおよびprotoconn_test.go内のテストにおいて、runtime.GOOS(実行中のOS)に基づいてテストをスキップするロジックが追加されました。これにより、特定のOSで実行できない、または異なる挙動を示すテストが、そのOSではスキップされるようになり、テストスイート全体の安定性が向上しました。例えば、Plan 9ではRaw IPテストをスキップし、Windowsではroot権限チェックをスキップする(Windowsでは権限昇格のメカニズムが異なるため)といった変更が行われています。

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

このコミットによる主要なコード変更は以下の3つのファイルにわたります。

  1. src/pkg/net/fd_windows.go:

    • func canUseConnectEx(net string) bool 関数が変更されました。
    • 変更前: if net == "udp" || net == "udp4" || net == "udp6" { return false }
    • 変更後: switch net { case "udp", "udp4", "udp6", "ip", "ip4", "ip6": return false }
      • "ip", "ip4", "ip6" (Raw IPネットワークタイプ) がConnectExを使用しないリストに追加されました。
  2. src/pkg/net/ipraw_test.go:

    • TestConnICMPEcho および TestPacketConnICMPEcho 関数において、OSごとのテストスキップロジックが追加されました。
      • os.Getuid() != 0 のチェックが switch runtime.GOOS ブロック内に移動し、"plan9" および "windows" ではスキップされるようになりました(Windowsではroot権限チェックが不要なため)。
    • ICMPメッセージの送受信バッファの扱いが変更されました。
      • 送信バッファ変数名が b から wb (write buffer) に変更されました。
      • 受信バッファ rb (read buffer) が新たに導入され、そのサイズが make([]byte, 20+len(wb)) のように、送信データグラムのサイズにIPヘッダの最大長(20バイト)を加えたものとして確保されるようになりました。
      • c.Read(b)c.ReadFrom(b) の呼び出しが c.Read(rb)c.ReadFrom(rb) に変更されました。
      • ipv4Payload(b)parseICMPMessage(b) の呼び出しが ipv4Payload(rb)parseICMPMessage(rb) に変更されました。
    • TestIPConnLocalName および TestIPConnRemoteName 関数において、"plan9" および "windows" でテストをスキップするロジックが追加されました。
  3. src/pkg/net/protoconn_test.go:

    • TestIPConnSpecificMethods 関数において、OSごとのテストスキップロジックが変更されました。
      • os.Getuid() != 0 のチェックが switch runtime.GOOS ブロック内に移動し、"plan9" ではスキップされ、"windows" ではroot権限チェックがスキップされるようになりました。
    • 受信バッファのサイズ計算が変更されました。
      • rb := make([]byte, 20+128) から rb := make([]byte, 20+len(wb)) に変更され、送信データグラムのサイズに基づいて受信バッファが確保されるようになりました。

コアとなるコードの解説

src/pkg/net/fd_windows.go の変更

canUseConnectEx関数は、Windows上でConnectEx APIを使用できるネットワークタイプを判断します。ConnectExはコネクション指向ソケット(TCPなど)向けであり、コネクションレスソケット(UDP、Raw IP)には適していません。この変更により、"ip", "ip4", "ip6"といったRaw IPネットワークタイプが明示的にConnectExの使用対象から除外されました。これにより、GoのnetパッケージがWindows上でRaw IPソケットを扱う際に、誤ってConnectExを呼び出してエラーになることを防ぎ、Raw IPネットワークへのダイヤルが正しく機能するようになります。

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

このファイルはRaw IPソケットのテストを含んでいます。

  1. OSごとのテストスキップ:
    • TestConnICMPEcho, TestPacketConnICMPEcho, TestIPConnLocalName, TestIPConnRemoteNameといったテスト関数に、switch runtime.GOOSによるOS判定が追加されました。
    • "plan9"ではRaw IPソケットのサポートが異なるため、これらのテストはスキップされます。
    • "windows"では、os.Getuid() != 0(root権限チェック)がスキップされます。これは、WindowsのセキュリティモデルがUnix系OSとは異なり、Raw IPソケットの操作に必要な権限昇格のメカニズムが異なるためです。これにより、Windows環境でのテスト実行がスムーズになります。
  2. データグラム受信バッファの改善:
    • 最も重要な変更は、ICMP Echoテストにおける受信バッファのサイズ計算です。以前は、受信バッファのサイズが固定であったり、送信データグラムのサイズに依存しすぎていたりする可能性がありました。
    • rb := make([]byte, 20+len(wb))という変更により、受信バッファrbは、送信されたICMPメッセージwbのサイズに、IPヘッダの最大長である20バイトを加えたサイズで確保されるようになりました。
    • Raw IPソケットでパケットを受信する場合、OSはIPヘッダを含む完全なIPパケットをアプリケーションに渡すことがあります。そのため、ICMPメッセージ本体だけでなく、その上位にあるIPヘッダ分の領域も考慮してバッファを確保する必要があります。この変更により、Windowsを含む様々なプラットフォームで、受信したIPパケットが切り捨てられることなく、完全な形でアプリケーションに渡されることが保証され、テストの信頼性と正確性が向上しました。

src/pkg/net/protoconn_test.go の変更

このファイルもネットワークプロトコル関連のテストを含んでいます。

  1. OSごとのテストスキップ:
    • TestIPConnSpecificMethods関数でも、switch runtime.GOOSによるOS判定が導入されました。"plan9"ではテストがスキップされ、"windows"ではroot権限チェックがスキップされるようになりました。これはipraw_test.goと同様の理由です。
  2. 受信バッファのサイズ計算:
    • rb := make([]byte, 20+len(wb))という変更は、ipraw_test.goと同様に、受信バッファのサイズを送信データグラムのサイズとIPヘッダの最大長に基づいて動的に決定することで、データグラムの切り捨てを防ぎ、テストの堅牢性を高めています。

これらの変更は、GoのnetパッケージがWindowsを含む多様なOS環境でRaw IPネットワーク機能をより堅牢かつ正確に提供するための重要なステップです。

関連リンク

参考にした情報源リンク