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

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

このコミットは、Go言語の標準ライブラリnetパッケージにおいて、Plan 9オペレーティングシステム向けのlookupProtocol関数を追加するものです。この関数は、IPプロトコル名(例: "tcp", "udp")を受け取り、それに対応するIPプロトコル番号を返します。この変更は、TestResolveIPAddrなどの既存のテストがPlan 9環境で正しく動作するために必要とされました。

コミット

commit a1b2d1404be98284416e2a7ea6f55bc129222cc0
Author: Akshat Kumar <seed@mail.nanosouffle.net>
Date:   Tue Mar 12 23:05:39 2013 +0100

    net: Plan 9: add lookupProtocol
    
    Needed by TestResolveIPAddr. This makes us pass tests
    again.
    
    R=rsc, rminnich, ality, bradfitz
    CC=golang-dev
    https://golang.org/cl/7737043

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

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

元コミット内容

net: Plan 9: add lookupProtocol
Needed by TestResolveIPAddr. This makes us pass tests
again.
R=rsc, rminnich, ality, bradfitz
CC=golang-dev
https://golang.org/cl/7737043

変更の背景

このコミットの主な背景は、Go言語のnetパッケージがPlan 9オペレーティングシステム上で正しく機能し、既存のテスト(特にTestResolveIPAddr)をパスできるようにすることでした。

Go言語のnetパッケージは、ネットワークアドレスの解決、接続の確立、データの送受信など、様々なネットワーク操作を提供します。これらの操作は、基盤となるオペレーティングシステムのネットワーク機能に依存しています。Plan 9は、その独特な設計思想、特に「すべてがファイルである」という原則に基づいており、ネットワークインターフェースもファイルシステムを通じて抽象化されています。

以前のlookupProtocolの実装は、TODO: Implement thisというコメントと共にsyscall.EPLAN9エラーを返すスタブ状態でした。これは、プロトコル名をプロトコル番号に変換する機能がPlan 9環境では未実装であったことを意味します。TestResolveIPAddrのようなテストは、内部的にこのプロトコルルックアップ機能を利用している可能性があり、その未実装がテスト失敗の原因となっていました。

このコミットは、Plan 9のネットワークインターフェース(特に/net/csファイル)を利用してlookupProtocolを実装することで、この問題を解決し、GoのnetパッケージがPlan 9上で完全に機能するようにすることを目的としています。

前提知識の解説

Plan 9オペレーティングシステム

Plan 9 from Bell Labsは、Unixの後継として設計された分散オペレーティングシステムです。その最も特徴的な設計原則は「すべてがファイルである」というもので、デバイス、プロセス、ネットワーク接続など、システム内のあらゆるリソースがファイルシステムを通じてアクセスされます。これにより、シンプルで一貫性のあるインターフェースが提供され、分散システムでのリソース共有が容易になります。

Plan 9のネットワークは、特にこのファイルシステム原則に深く統合されています。ネットワークプロトコルスタックや接続情報は、/netディレクトリ以下の特殊なファイルを通じて公開されます。例えば、/net/tcp/net/udpといったディレクトリがあり、それぞれのプロトコルに関する情報や操作が可能です。

Go言語のnetパッケージ

Go言語の標準ライブラリnetパッケージは、TCP/IPネットワークプログラミングのための強力な機能を提供します。IPアドレスの解決(DNSルックアップ)、TCP/UDP接続の確立、HTTPクライアント/サーバーの実装など、幅広いネットワーク操作をサポートします。netパッケージは、クロスプラットフォームで動作するように設計されており、各OSのネイティブなネットワークAPIを抽象化して提供します。

IPプロトコル番号

IP(Internet Protocol)は、インターネットにおけるデータパケットのルーティングを司るプロトコルです。IPパケットのヘッダーには「プロトコル番号」というフィールドがあり、そのIPパケットが上位層のどのプロトコル(例: TCP、UDP、ICMP)のデータを含んでいるかを示します。例えば、TCPはプロトコル番号6、UDPはプロトコル番号17です。これらの番号はIANA(Internet Assigned Numbers Authority)によって管理されています。

Plan 9の/net/cs (Connection Server)

Plan 9の/net/csファイルは、"Connection Server"または"Connect Service"として機能します。これは、ネットワークサービスに関する情報を問い合わせるためのインターフェースを提供します。クライアントは、このファイルに対して特定の形式のクエリを書き込むことで、IPアドレスの解決、プロトコル情報の取得、ポート番号のルックアップなどを行うことができます。

クエリの形式は通常、!key=valueのような形式で、特定の情報を要求します。例えば、!protocol=tcpというクエリは、"tcp"というプロトコル名に関する情報を要求します。/net/csは、このクエリに応答して、関連する情報を含む行を返します。この情報は、通常、スペース区切りのフィールドで構成され、プロトコル番号などの詳細が含まれます。

技術的詳細

このコミットで追加されたlookupProtocol関数は、Plan 9環境においてIPプロトコル名から対応するプロトコル番号を取得する役割を担います。その実装は、Plan 9の「すべてがファイルである」という原則と、/net/csインターフェースを最大限に活用しています。

  1. /net/csへのクエリ: lookupProtocol関数は、まずquery関数を呼び出して/net/csファイルに問い合わせを行います。クエリ文字列は"!protocol=" + nameという形式です。ここでnameは、ルックアップしたいプロトコル名(例: "tcp", "udp")です。このクエリは、/net/csに対して、指定されたプロトコル名に関する情報を要求します。

  2. 応答の取得とエラーハンドリング: query関数は、/net/csからの応答を行の配列として返します。エラーが発生した場合(例: ファイルの読み取りエラー、ネットワークの問題)、lookupProtocolはそのままエラーを返します。 応答が空の場合(len(lines) == 0)、または応答の形式が期待通りでない場合(len(f) < 2)、"unknown IP protocol specified"というエラーを返します。これは、指定されたプロトコル名が見つからなかったことを示します。

  3. 応答のパース: /net/csからの応答は、通常、スペース区切りのフィールドを持つ行です。lookupProtocolは、getFields(lines[0])を使って応答の最初の行をフィールドに分割します。 プロトコル番号は、これらのフィールドの2番目(インデックス1)に位置する文字列sから抽出されます。この文字列はkey=valueの形式(例: proto=6)であると想定されます。 dtoi(s, byteIndex(s, '=')+1)は、この文字列sから=記号の後の数値部分を整数に変換するヘルパー関数です。dtoiは、変換された数値、残りの文字列の長さ、および変換が成功したかどうかを示すブール値を返します。

  4. プロトコル番号の返却: dtoiが成功した場合(oktrue)、変換された数値nがプロトコル番号として返されます。 変換が失敗した場合、または応答の形式が不正な場合は、再度"unknown IP protocol specified"エラーが返されます。

この実装により、GoのnetパッケージはPlan 9のネイティブなネットワーク情報取得メカニズムを利用して、プロトコルルックアップを正確に行うことができるようになります。

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

--- a/src/pkg/net/lookup_plan9.go
+++ b/src/pkg/net/lookup_plan9.go
@@ -7,7 +7,6 @@ package net
 import (
 	"errors"
 	"os"
-	"syscall"
 )
 
 func query(filename, query string, bufSize int) (res []string, err error) {
@@ -70,9 +69,26 @@ func queryDNS(addr string, typ string) (res []string, err []error) {
 	return query("/net/dns", addr+" "+typ, 1024)
 }
 
+// lookupProtocol looks up IP protocol name and returns
+// the corresponding protocol number.
 func lookupProtocol(name string) (proto int, err error) {
-	// TODO: Implement this
-	return 0, syscall.EPLAN9
+	lines, err := query("/net/cs", "!protocol="+name, 128)
+	if err != nil {
+		return 0, err
+	}
+	unknownProtoError := errors.New("unknown IP protocol specified: " + name)
+	if len(lines) == 0 {
+		return 0, unknownProtoError
+	}
+	f := getFields(lines[0])
+	if len(f) < 2 {
+		return 0, unknownProtoError
+	}
+	s := f[1]
+	if n, _, ok := dtoi(s, byteIndex(s, '=')+1); ok {
+		return n, nil
+	}
+	return 0, unknownProtoError
 }
 
 func lookupHost(host string) (addrs []string, err error) {

コアとなるコードの解説

変更は主にsrc/pkg/net/lookup_plan9.goファイル内のlookupProtocol関数に集中しています。

  1. syscallパッケージの削除: - "syscall" 以前のスタブ実装でsyscall.EPLAN9を使用していたためインポートされていましたが、新しい実装では不要になったため削除されました。

  2. lookupProtocol関数の実装: func lookupProtocol(name string) (proto int, err error) この関数は、プロトコル名(name)を受け取り、対応するプロトコル番号(proto)とエラー(err)を返します。

    // TODO: Implement this // return 0, syscall.EPLAN9 以前の未実装のスタブコードが削除され、実際のロジックが追加されました。

    lines, err := query("/net/cs", "!protocol="+name, 128) query関数を呼び出し、Plan 9の接続サーバー/net/csに対してクエリを実行します。クエリ文字列は"!protocol=" + nameで、指定されたプロトコル名に関する情報を要求します。128は応答バッファのサイズを示します。

    if err != nil { return 0, err } query関数がエラーを返した場合、そのエラーをそのまま返します。

    unknownProtoError := errors.New("unknown IP protocol specified: " + name) プロトコルが見つからなかった場合や、応答のパースに失敗した場合に返す共通のエラーメッセージを定義しています。

    if len(lines) == 0 { return 0, unknownProtoError } /net/csからの応答が空の場合、指定されたプロトコルが見つからなかったと判断し、unknownProtoErrorを返します。

    f := getFields(lines[0]) 応答の最初の行(lines[0])をスペースで分割し、フィールドの配列fを取得します。Plan 9の/net/csからの応答は通常、スペース区切りのデータを含みます。

    if len(f) < 2 { return 0, unknownProtoError } フィールドの数が2未満の場合、応答の形式が不正であると判断し、unknownProtoErrorを返します。プロトコル番号は通常、2番目のフィールドに含まれるため、最低2つのフィールドが必要です。

    s := f[1] プロトコル番号が含まれると期待される2番目のフィールド(インデックス1)の文字列をsに代入します。

    if n, _, ok := dtoi(s, byteIndex(s, '=')+1); ok { return n, nil } dtoi関数は、文字列sから数値を解析します。byteIndex(s, '=')+1は、文字列s内の=文字の次の位置から解析を開始するように指定しています。これは、proto=6のような形式の文字列から6を抽出するためです。 dtoiは、解析された数値n、残りの文字列の長さ(ここでは不要なので_で無視)、および解析が成功したかどうかを示すブール値okを返します。 oktrueの場合、解析が成功したことを意味し、n(プロトコル番号)とnilエラーを返します。

    return 0, unknownProtoError dtoiによる数値解析が失敗した場合(okfalse)、unknownProtoErrorを返します。

この実装により、GoのnetパッケージはPlan 9のファイルシステムベースのネットワークインターフェースを介して、IPプロトコル名をその数値表現に変換できるようになり、TestResolveIPAddrのようなテストが正しく動作するようになりました。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード(特にsrc/pkg/net/lookup_plan9.goの変更履歴)
  • Plan 9のドキュメントと関連資料
  • IPプロトコル番号に関する一般的な情報源(IANAなど)
  • Go言語のコードレビューシステム(Gerrit)のCL (Change List) 7737043: https://golang.org/cl/7737043 (コミットメッセージに記載されているリンク)