[インデックス 17361] ファイルの概要
このコミットは、Go言語のnet
パッケージにおいて、インターネットプロトコル番号に関する最小限の情報を追加するものです。これは、完全なプロトコル番号リストを持たないプラットフォームをサポートすることを目的としています。具体的には、主要なプロトコル(ICMP, IGMP, TCP, UDP, IPv6-ICMP)の名称と番号の対応をハードコードしたマップを導入し、既存のプロトコル読み込みロジック(特にUnix系システムにおける/etc/protocols
の読み込み)と統合することで、より堅牢なプロトコル解決メカニズムを提供します。
コミット
commit fd58320f32a1b8f329e741ab10f1a552a06ede4a
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Thu Aug 22 10:33:37 2013 +0900
net: add minimal internet protocol number information base
This CL adds minimal information for supporting platforms that don't
have a complete list of internet protocol numbers.
Fixes #5344.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/12898045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fd58320f32a1b8f329e741ab10f1a552a06ede4a
元コミット内容
net: add minimal internet protocol number information base
This CL adds minimal information for supporting platforms that don't
have a complete list of internet protocol numbers.
Fixes #5344.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/12898045
変更の背景
Go言語のnet
パッケージは、ネットワークI/Oを扱うための移植性の高いインターフェースを提供します。これには、TCP/IP、UDP、IPネットワークなどが含まれます。ネットワークプロトコルを識別するために、通常はプロトコル名(例: "tcp", "udp")または対応するインターネットプロトコル番号(例: TCPは6、UDPは17)が使用されます。
しかし、一部のプラットフォームでは、システムが提供するプロトコル番号のリストが不完全であるか、あるいは全く提供されていない場合があります。このような環境では、Goのnet
パッケージがプロトコル名を正しく解決できず、ネットワーク通信に問題が生じる可能性がありました。
このコミットは、この問題を解決するために導入されました。主要なインターネットプロトコル(ICMP, IGMP, TCP, UDP, IPv6-ICMP)について、Go自身が最小限のプロトコル名と番号の対応表を持つことで、システムに依存せずにこれらの基本的なプロトコルを確実に識別できるようにすることが目的です。これにより、Goアプリケーションの移植性と堅牢性が向上します。コミットメッセージにあるFixes #5344
は、この変更が特定の課題(Issue 5344)を解決することを示唆しています。
前提知識の解説
インターネットプロトコル番号 (IANA Protocol Numbers)
インターネットプロトコル番号は、Internet Assigned Numbers Authority (IANA) によって管理される8ビットの数値で、IPパケットのIPヘッダ(IPv4のProtocolフィールドまたはIPv6のNext Headerフィールド)にカプセル化される次のレベルのプロトコルを識別するために使用されます。これにより、ネットワークデバイスはIPヘッダの後に続くデータを正しく解釈できます。
- 目的: IPパケットが運ぶペイロードがどのプロトコル(例: TCP, UDP, ICMP)のものであるかを識別します。
- 範囲: 0から255までの256個の番号があります。
- 主要なプロトコル番号の例:
- 1: ICMP (Internet Control Message Protocol)
- 2: IGMP (Internet Group Management Protocol)
- 6: TCP (Transmission Control Protocol)
- 17: UDP (User Datagram Protocol)
- 58: ICMPv6 (ICMP for IPv6)
- IANAの役割: IANAは、これらのプロトコル番号の公式なレジストリを維持しており、
www.iana.org/assignments/protocol-numbers
で公開されています。
Go言語のnet
パッケージとプロトコル解決
Goのnet
パッケージは、ネットワークアドレスやプロトコル名を解決する機能を提供します。例えば、net.Dial
やnet.ListenIP
のような関数では、ネットワークタイプを指定する際にプロトコル名(例: "tcp")またはプロトコル番号(例: "6")を使用できます。
通常、Goはシステムが提供する情報源(Unix系システムでは/etc/protocols
ファイルなど)からプロトコル名と番号の対応を読み込みます。しかし、前述の通り、これらの情報源が不完全な場合があるため、Go自身が基本的なプロトコル情報を内部に持つことで、より信頼性の高い解決を可能にします。
/etc/protocols
ファイル
Unix系オペレーティングシステムでは、/etc/protocols
ファイルは、インターネットプロトコル名とその対応するプロトコル番号、およびオプションのエイリアスを定義するテキストファイルです。このファイルは、システム上のネットワークアプリケーションがプロトコル名を数値に変換するために使用されます。
例:
# /etc/protocols:
# $Id: protocols,v 1.1 2000/07/11 19:00:00 lukem Exp $
#
# Internet (IP) protocols
#
ip 0 IP # internet protocol, pseudo protocol number
icmp 1 ICMP # internet control message protocol
igmp 2 IGMP # internet group management protocol
ggp 3 GGP # gateway-gateway protocol
ipencap 4 IP-ENCAP # IP encapsulated in IP (RFC2003)
st 5 ST # ST datagram mode
tcp 6 TCP # transmission control protocol
egp 8 EGP # exterior gateway protocol
pup 12 PUP # PARC universal packet protocol
udp 17 UDP # user datagram protocol
hmp 20 HMP # host monitoring protocol
xns-idp 22 XNS-IDP # Xerox NS IDP
rdp 27 RDP # "reliable datagram" protocol
ipv6 41 IPv6 # Internet Protocol, Version 6
ipv6-route 43 IPv6-Route # Routing Header for IPv6
ipv6-frag 44 IPv6-Frag # Fragment Header for IPv6
gre 47 GRE # Generic Routing Encapsulation
esp 50 ESP # Encap Security Payload
ah 51 AH # Authentication Header
ipv6-icmp 58 IPv6-ICMP # ICMP for IPv6
ipv6-nonxt 59 IPv6-NoNxt # No Next Header for IPv6
ipv6-opts 60 IPv6-Opts # Destination Options for IPv6
Goのnet
パッケージは、このファイルを読み込んでプロトコル情報を取得しますが、このコミットにより、ファイルが存在しない、または不完全な場合でも基本的なプロトコルが認識されるようになります。
技術的詳細
このコミットの主要な技術的アプローチは以下の通りです。
-
ハードコードされたプロトコルマップの導入:
src/pkg/net/lookup.go
にprotocols
というグローバルマップを導入し、主要なインターネットプロトコル名とその番号の対応を初期値として設定します。これにより、システムが提供する情報に依存せず、Go自身が基本的なプロトコル情報を常に利用できるようになります。icmp
: 1igmp
: 2tcp
: 6udp
: 17ipv6-icmp
: 58- これらのプロトコル名は大文字・小文字を区別しないように、両方のケースがマップに含まれています(例: "icmp"と"ICMP")。
-
既存のプロトコル読み込みロジックの変更:
- Unix系システム (
src/pkg/net/lookup_unix.go
): 以前は/etc/protocols
を読み込む際にprotocols
マップをゼロから作成していましたが、この変更により、新しく導入されたグローバルなprotocols
マップに、/etc/protocols
から読み込んだ情報を追加する形になります。これにより、ハードコードされた情報が優先され、システムファイルの情報がそれにマージされる形になります。具体的には、/etc/protocols
から読み込んだプロトコル名が既にprotocols
マップに存在しない場合にのみ、その情報を追加します。これにより、Goが提供する基本的な情報が上書きされることを防ぎます。 - Windows系システム (
src/pkg/net/lookup_windows.go
): Windowsでは、lookupProtocol
関数がシステムコールを使用してプロトコル情報を取得します。このコミットでは、システムコールが失敗した場合(r.err != nil
)、新しく導入されたグローバルなprotocols
マップをフォールバックとして参照するように変更されました。これにより、システムがプロトコル情報を解決できない場合でも、Goが持つ最小限の情報で対応できるようになります。
- Unix系システム (
このアプローチにより、Goのnet
パッケージは、様々なプラットフォームにおいて、より堅牢かつ一貫したプロトコル解決能力を持つことになります。
コアとなるコードの変更箇所
このコミットでは、以下の3つのファイルが変更されています。
-
src/pkg/net/lookup.go
:protocols
というmap[string]int
型のグローバル変数が追加され、主要なインターネットプロトコル名とその番号の初期値が設定されています。
--- a/src/pkg/net/lookup.go +++ b/src/pkg/net/lookup.go @@ -8,6 +8,19 @@ import ( "time" ) +// protocols contains minimal mappings between internet protocol +// names and numbers for platforms that don't have a complete list of +// protocol numbers. +// +// See http://www.iana.org/assignments/protocol-numbers +var protocols = map[string]int{ + "icmp": 1, "ICMP": 1, + "igmp": 2, "IGMP": 2, + "tcp": 6, "TCP": 6, + "udp": 17, "UDP": 17, + "ipv6-icmp": 58, "IPV6-ICMP": 58, "IPv6-ICMP": 58, +} + var lookupGroup singleflight // lookupHostMerge wraps lookupHost, but makes sure that for any given
-
src/pkg/net/lookup_unix.go
:protocols
変数の宣言が削除され、グローバルなprotocols
マップを使用するように変更されています。readProtocols
関数内で、/etc/protocols
から読み込んだプロトコル情報を既存のprotocols
マップにマージするロジックが追加されています。具体的には、読み込んだプロトコル名がマップに存在しない場合にのみ追加されます。
--- a/src/pkg/net/lookup_unix.go +++ b/src/pkg/net/lookup_unix.go @@ -11,15 +11,11 @@ import ( "sync" ) -var ( - protocols map[string]int - onceReadProtocols sync.Once -) +var onceReadProtocols sync.Once // readProtocols loads contents of /etc/protocols into protocols map // for quick access. func readProtocols() { - protocols = make(map[string]int) if file, err := open("/etc/protocols"); err == nil { for line, ok := file.readLine(); ok; line, ok = file.readLine() { // tcp 6 TCP # transmission control protocol @@ -31,9 +27,13 @@ func readProtocols() { } if proto, _, ok := dtoi(f[1], 0); ok { - protocols[f[0]] = proto + if _, ok := protocols[f[0]]; !ok { + protocols[f[0]] = proto + } for _, alias := range f[2:] { - protocols[alias] = proto + if _, ok := protocols[alias]; !ok { + protocols[alias] = proto + } } } }
-
src/pkg/net/lookup_windows.go
:lookupProtocol
関数内で、システムコールがエラーを返した場合に、新しく導入されたグローバルなprotocols
マップをフォールバックとして参照するロジックが追加されています。
--- a/src/pkg/net/lookup_windows.go +++ b/src/pkg/net/lookup_windows.go @@ -42,6 +42,11 @@ func lookupProtocol(name string) (proto int, err error) { ch <- result{proto: proto, err: err} }() r := <-ch + if r.err != nil { + if proto, ok := protocols[name]; ok { + return proto, nil + } + } return r.proto, r.err }
コアとなるコードの解説
src/pkg/net/lookup.go
var protocols = map[string]int{...}
:- この行は、Goの
net
パッケージが内部的に使用するプロトコル名と番号の対応マップを定義しています。 icmp
,igmp
,tcp
,udp
,ipv6-icmp
といった基本的なプロトコルが、そのIANA割り当て番号と共に初期化されています。- 大文字と小文字の両方のプロトコル名が登録されているのは、プロトコル名の検索時に大文字・小文字を区別しない柔軟性を提供するためです。
- このマップは、システムが提供するプロトコル情報が不完全な場合や利用できない場合のフォールバックとして機能します。
- この行は、Goの
src/pkg/net/lookup_unix.go
var onceReadProtocols sync.Once
:sync.Once
は、readProtocols
関数が複数回呼び出されても、その中の処理(/etc/protocols
の読み込み)が一度だけ実行されることを保証するためのGoの標準ライブラリの機能です。これにより、リソースの無駄な消費を防ぎ、初期化処理の安全性を確保します。
func readProtocols() { ... }
:- この関数は、Unix系システムで
/etc/protocols
ファイルを読み込み、その内容をprotocols
マップに反映させる役割を担います。 - 変更点: 以前は
protocols = make(map[string]int)
でマップを初期化していましたが、この行が削除されました。これにより、src/pkg/net/lookup.go
で定義されたグローバルなprotocols
マップがそのまま使用されるようになります。 - マージロジック:
if proto, _, ok := dtoi(f[1], 0); ok { if _, ok := protocols[f[0]]; !ok { protocols[f[0]] = proto } for _, alias := range f[2:] { if _, ok := protocols[alias]; !ok { protocols[alias] = proto } } }
- この部分が、
/etc/protocols
から読み込んだプロトコル情報(f[0]
はプロトコル名、f[1]
は番号、f[2:]
はエイリアス)をprotocols
マップに追加するロジックです。 if _, ok := protocols[f[0]]; !ok
という条件は、「もしprotocols
マップにf[0]
(プロトコル名)がまだ存在しないならば」という意味です。これにより、src/pkg/net/lookup.go
でハードコードされた基本的なプロトコル情報が、/etc/protocols
の内容によって上書きされることを防ぎます。システムファイルに同じプロトコルが定義されていても、Goが持つ情報が優先されます。- エイリアスについても同様のチェックが行われ、既存のエイリアスが上書きされないように配慮されています。
- この部分が、
- この関数は、Unix系システムで
src/pkg/net/lookup_windows.go
func lookupProtocol(name string) (proto int, err error) { ... }
:- この関数は、Windowsシステムでプロトコル名を解決するために使用されます。通常、システムコール(
lookupProtocol
の内部で呼び出されるlookup
関数)を通じてプロトコル情報を取得します。 - 変更点:
if r.err != nil { if proto, ok := protocols[name]; ok { return proto, nil } }
r.err != nil
は、システムコールによるプロトコル解決がエラーを返した場合(つまり、システムが指定されたプロトコル名を解決できなかった場合)を意味します。- この場合、
if proto, ok := protocols[name]; ok
という条件で、src/pkg/net/lookup.go
で定義されたグローバルなprotocols
マップに、要求されたプロトコル名(name
)が存在するかどうかを確認します。 - もし存在すれば、そのハードコードされたプロトコル番号(
proto
)を返し、エラーはnil
とすることで、解決が成功したと見なします。 - これにより、Windowsシステムがプロトコル名を解決できない場合でも、Goが持つ最小限のプロトコル情報で対応できるようになり、堅牢性が向上します。
- この関数は、Windowsシステムでプロトコル名を解決するために使用されます。通常、システムコール(
これらの変更により、Goのnet
パッケージは、OSのプロトコル情報に完全に依存することなく、主要なプロトコルを確実に識別できるようになり、異なるプラットフォームでの動作の安定性が向上しました。
関連リンク
- Go Issue #5344: コミットメッセージに
Fixes #5344
とありますが、Goの公式Issueトラッカーでこの番号のIssueを直接特定することはできませんでした。しかし、このコミットが「完全なプロトコル番号リストを持たないプラットフォームをサポートする」という目的を明記していることから、特定の環境でのプロトコル解決に関する問題が背景にあったと考えられます。 - IANA Protocol Numbers Registry: https://www.iana.org/assignments/protocol-numbers - インターネットプロトコル番号の公式なレジストリ。
参考にした情報源リンク
- Go
net
package documentation: https://pkg.go.dev/net - IANA Protocol Numbers: https://www.iana.org/assignments/protocol-numbers
golang.org/x/net/internal/iana
package (for IANA protocol constants): https://pkg.go.dev/golang.org/x/net/internal/iananet
package network specification examples: https://go.dev/doc/go1.1#net (Go 1.1 release notes, which might contain relevant context for oldernet
package behavior)/etc/protocols
file explanation: https://www.mystrika.com/iana-protocol-numbers/ (General explanation of IANA protocol numbers and/etc/protocols
)- Go
net
package and protocol numbers: https://sobyte.net/post/2022-03/go-net-package-protocol-numbers/ (Discusses how Go'snet
package handles protocol numbers)