[インデックス 15913] ファイルの概要
このコミットは、Go言語の標準ライブラリであるnet
パッケージにおいて、IPv6スコープ付きアドレス(Scoped Addressing)ゾーンのサポートを追加するものです。これにより、リンクローカルアドレスなどの非グローバルIPv6アドレスが、どのネットワークインターフェースに属するかを示すゾーン識別子(Zone ID)を伴って扱えるようになります。
コミット
commit aa0dda767a9a31c6b0b49a665f0d059ebfed8e1c
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Sat Mar 23 09:57:40 2013 +0900
net: support IPv6 scoped addressing zone
This CL provides IPv6 scoped addressing zone support as defined
in RFC 4007 for internet protocol family connection setups.
Follwoing types and functions allow a literal IPv6 address with
zone identifer as theirs parameter.
pkg net, func Dial(string, string) (Conn, error)
pkg net, func DialOpt(string, ...DialOption) (Conn, error)
pkg net, func DialTimeout(string, string, time.Duration) (Conn, error)
pkg net, func Listen(string, string) (Listener, error)
pkg net, func ListenPacket(string, string) (PacketConn, error)
pkg net, func ResolveIPAddr(string, string) (*IPAddr, error)
pkg net, func ResolveTCPAddr(string, string) (*TCPAddr, error)
pkg net, func ResolveUDPAddr(string, string) (*UDPAddr, error)
pkg net, type IPAddr struct, Zone string
pkg net, type TCPAddr struct, Zone string
pkg net, type UDPAddr struct, type Zone string
Also follwoing methods return a literal IPv6 address with zone
identifier string if possible.
pkg net, method (*IPAddr) String() string
pkg net, method (*TCPAddr) String() string
pkg net, method (*UDPAddr) String() string
Fixes #4234.
Fixes #4501.
Update #5081.
R=rsc, iant
CC=golang-dev
https://golang.org/cl/6816116
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/aa0dda767a9a31c6b0b49a665f0d059ebfed8e1c
元コミット内容
net: support IPv6 scoped addressing zone
This CL provides IPv6 scoped addressing zone support as defined
in RFC 4007 for internet protocol family connection setups.
Follwoing types and functions allow a literal IPv6 address with
zone identifer as theirs parameter.
pkg net, func Dial(string, string) (Conn, error)
pkg net, func DialOpt(string, ...DialOption) (Conn, error)
pkg net, func DialTimeout(string, string, time.Duration) (Conn, error)
pkg net, func Listen(string, string) (Listener, error)
pkg net, func ListenPacket(string, string) (PacketConn, error)
pkg net, func ResolveIPAddr(string, string) (*IPAddr, error)
pkg net, func ResolveTCPAddr(string, string) (*TCPAddr, error)
pkg net, func ResolveUDPAddr(string, string) (*UDPAddr, error)
pkg net, type IPAddr struct, Zone string
pkg net, type TCPAddr struct, Zone string
pkg net, type UDPAddr struct, Zone string
Also follwoing methods return a literal IPv6 address with zone
identifier string if possible.
pkg net, method (*IPAddr) String() string
pkg net, method (*TCPAddr) String() string
pkg net, method (*UDPAddr) String() string
Fixes #4234.
Fixes #4501.
Update #5081.
R=rsc, iant
CC=golang-dev
https://golang.org/cl/6816116
変更の背景
このコミットは、Go言語のnet
パッケージがIPv6のスコープ付きアドレス(Scoped Addressing)を適切に処理できるようにするために導入されました。特に、RFC 4007で定義されているIPv6スコープ付きアドレスのゾーン識別子(Zone ID)のサポートが主な目的です。
従来のGoのnet
パッケージでは、IPv6のリンクローカルアドレス(例: fe80::1
)のような非グローバルアドレスを扱う際に問題がありました。これらのアドレスは、異なるネットワークインターフェース(例: eth0
, lo0
)上で同じアドレスが使用される可能性があるため、どのインターフェースに属するかを明示的に指定しないと、システムが正しいインターフェースを選択できず、通信が失敗する可能性がありました。
この問題は、以下のGoのIssueで報告されていました。
- Issue #4234: IPv6リンクローカルアドレスのゾーン識別子をサポートする必要性。
- Issue #4501:
net.Dial
やnet.Listen
などの関数で、IPv6アドレスにゾーン識別子を含めることができない問題。 - Issue #5081: IPv6アドレスの文字列表現にゾーン識別子を含めるべきであるという提案。
これらの問題を解決し、IPv6の完全な機能性をGoアプリケーションで利用できるようにするために、このコミットがnet
パッケージにゾーン識別子のサポートを導入しました。これにより、開発者はIPv6リンクローカルアドレスをより堅牢に扱うことができるようになります。
前提知識の解説
IPv6アドレスのスコープとゾーン
IPv6アドレスには「スコープ(Scope)」という概念があります。これは、アドレスが有効な範囲を示すもので、主に以下の種類があります。
- インターフェースローカルスコープ (Interface-Local Scope): アドレスが単一のインターフェース上でのみ有効。主にマルチキャストアドレスで使用されます。
- リンクローカルスコープ (Link-Local Scope): アドレスが単一のリンク(ネットワークセグメント)上でのみ有効。ルーターを越えて転送されることはありません。
fe80::/10
のプレフィックスを持つアドレスがこれに該当します。 - ユニークローカルスコープ (Unique Local Scope): プライベートネットワーク内でユニークなアドレス。グローバルルーティングはされません。
fc00::/7
のプレフィックスを持つアドレスがこれに該当します。 - グローバルスコープ (Global Scope): インターネット全体でユニークなアドレス。通常のインターネット通信で使用されます。
「ゾーン(Zone)」とは、特定のアドレススコープに対応するネットワークトポロジーの接続された領域を指します。例えば、リンクローカルスコープの場合、各物理リンクが独立したゾーンを形成します。
ゾーン識別子 (Zone ID / Scope ID)
リンクローカルアドレスのような非グローバルスコープのアドレスは、異なるゾーン(異なるネットワークインターフェース)で同じアドレスが使用される可能性があります。例えば、fe80::1
というアドレスがeth0
インターフェースとwlan0
インターフェースの両方に存在し得ます。
このような曖昧さを解消し、特定のインターフェースを明示するために使用されるのが「ゾーン識別子」です。ゾーン識別子は、IPv6アドレスの末尾に%
記号を付けて追加されます。例えば、fe80::1%eth0
は、eth0
インターフェース上のfe80::1
アドレスを指します。ゾーン識別子には、インターフェース名(例: eth0
, lo0
)や、システムが割り当てるインターフェースインデックス(例: 911
)が使用されます。
RFC 4007: IPv6 Scoped Address Architecture
RFC 4007は、「IPv6 Scoped Address Architecture」と題され、IPv6アドレスのスコープ、ゾーン、およびゾーン識別子の概念を詳細に定義しています。このRFCは、IPv6ネットワークにおけるアドレスの振る舞いと、非グローバルアドレスの曖昧さを解決するためのメカニズムを標準化しています。このコミットは、このRFC 4007の定義に基づいてGoのnet
パッケージにゾーン識別子のサポートを実装しています。
技術的詳細
このコミットは、Goのnet
パッケージにおけるIPv6アドレスのパース、表現、およびネットワーク操作にゾーン識別子のサポートを統合しています。
主な変更点は以下の通りです。
-
IPAddr
,TCPAddr
,UDPAddr
構造体へのZone
フィールドの追加: これらのアドレス構造体にZone string
フィールドが追加され、IPv6アドレスに関連付けられたゾーン識別子を保持できるようになりました。// pkg net, type IPAddr struct, Zone string // pkg net, type TCPAddr struct, Zone string // pkg net, type UDPAddr struct, Zone string
-
parseIPv6
関数の変更: IPv6アドレス文字列をパースするparseIPv6
関数が、ゾーン識別子を処理できるように変更されました。この関数は、アドレスとゾーンの両方を返すようになりました。// func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string)
zoneAllowed
引数が追加され、ゾーン識別子のパースを制御します。 -
splitHostZone
関数の追加: ホスト文字列からゾーン識別子を分離するための新しいヘルパー関数splitHostZone
が追加されました。// func splitHostZone(s string) (host, zone string)
-
JoinHostPort
関数の変更: ホストとポートを結合してアドレス文字列を生成するJoinHostPort
関数が、ホストにコロンまたはパーセント記号(ゾーン識別子)が含まれる場合に、ホスト部分を角括弧で囲むように変更されました。// func JoinHostPort(host, port string) string // If host has colons or a percent sign, have to bracket it. // if byteIndex(host, ':') >= 0 || byteIndex(host, '%') >= 0 { // return "[" + host + "]:" + port // }
-
SplitHostPort
関数の変更: アドレス文字列をホストとポートに分割するSplitHostPort
関数が、[ipv6-host%zone]:port
形式を認識し、ゾーン識別子を適切に処理するように変更されました。 -
Dial
,Listen
,ListenPacket
関数の変更: これらのネットワーク接続確立関数が、引数として渡されるアドレス文字列にゾーン識別子が含まれている場合、それを適切にパースして利用するように内部的に変更されました。 -
ResolveIPAddr
,ResolveTCPAddr
,ResolveUDPAddr
関数の変更: これらのアドレス解決関数が、ゾーン識別子を含むアドレス文字列を解決し、結果のIPAddr
,TCPAddr
,UDPAddr
構造体にゾーン情報を設定するように変更されました。 -
String()
メソッドの変更:(*IPAddr).String()
,(*TCPAddr).String()
,(*UDPAddr).String()
メソッドが、Zone
フィールドに値が設定されている場合に、IPv6アドレスにゾーン識別子を付加した文字列を返すように変更されました。// Example for IPAddr: // func (a *IPAddr) String() string { // if a == nil { // return "<nil>" // } // if a.Zone != "" { // return a.IP.String() + "%" + a.Zone // } // return a.IP.String() // }
これらの変更により、Goのnet
パッケージはIPv6のスコープ付きアドレスをより正確かつ柔軟に扱えるようになり、特にリンクローカルアドレスを用いた通信の信頼性が向上しました。
コアとなるコードの変更箇所
このコミットでは、主にsrc/pkg/net
ディレクトリ内の以下のファイルが変更されています。
src/pkg/net/dial.go
:Dial
,Listen
,ListenPacket
関数のコメントが更新され、IPv6スコープ付きアドレスの構文例が追加されました。内部的なアドレス解決ロジックもゾーン識別子を考慮するように調整されています。src/pkg/net/interface_test.go
: IPv6リンクローカルユニキャストアドレスを取得するためのヘルパー関数ipv6LinkLocalUnicastAddr
が追加され、テストケースが更新されました。src/pkg/net/ip.go
:IPAddr
,TCPAddr
,UDPAddr
構造体にZone
フィールドが追加されました。parseIPv6
関数がゾーン識別子をパースできるように変更され、ip IP, zone string
を返すようになりました。ParseIP
関数もparseIPv6
の変更に合わせて調整されました。
src/pkg/net/ip_test.go
:ParseIP
およびSplitHostPort
,JoinHostPort
のテストケースに、ゾーン識別子を含むIPv6アドレスのテストが追加されました。src/pkg/net/ipraw_test.go
:ResolveIPAddr
のテストケースにゾーン識別子を含むアドレスのテストが追加されました。src/pkg/net/iprawsock.go
:(*IPAddr).String()
メソッドがゾーン識別子を考慮するように変更され、ResolveIPAddr
のコメントが更新されました。src/pkg/net/ipsock.go
:SplitHostPort
関数がゾーン識別子を処理できるように変更され、missingBrackets
エラーケースが追加されました。splitHostZone
ヘルパー関数が追加されました。JoinHostPort
関数が、ホストにパーセント記号が含まれる場合も角括弧で囲むように変更されました。resolveInternetAddr
関数が、IPv6アドレスの解決時にゾーン識別子を考慮するように変更されました。
src/pkg/net/tcp_test.go
:ResolveTCPAddr
およびIPv6LinkLocalUnicastTCP
のテストケースが追加され、ゾーン識別子を含むTCPアドレスのテストが行われています。src/pkg/net/tcpsock.go
:(*TCPAddr).String()
メソッドがゾーン識別子を考慮するように変更され、ResolveTCPAddr
のコメントが更新されました。src/pkg/net/udp_test.go
:ResolveUDPAddr
およびIPv6LinkLocalUnicastUDP
のテストケースが追加され、ゾーン識別子を含むUDPアドレスのテストが行われています。src/pkg/net/udpsock.go
:(*UDPAddr).String()
メソッドがゾーン識別子を考慮するように変更され、ResolveUDPAddr
のコメントが更新されました。
コアとなるコードの解説
このコミットの核心は、IPv6アドレスの文字列表現にゾーン識別子を導入し、それをGoのnet
パッケージ全体で透過的に扱えるようにした点にあります。
最も重要な変更の一つは、src/pkg/net/ip.go
内のparseIPv6
関数のシグネチャ変更と実装です。
変更前:
func parseIPv6(s string) IP
変更後:
func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string)
この変更により、parseIPv6
はIPv6アドレスだけでなく、それに付随するゾーン識別子も抽出して返すようになりました。zoneAllowed
フラグは、ゾーン識別子のパースを許可するかどうかを制御します。
ゾーン識別子の抽出は、src/pkg/net/ipsock.go
に追加された新しいヘルパー関数splitHostZone
によって行われます。
func splitHostZone(s string) (host, zone string) {
// The IPv6 scoped addressing zone identifer starts after the
// last percent sign.
if i := last(s, '%'); i > 0 {
host, zone = s[:i], s[i+1:]
} else {
host = s
}
return
}
この関数は、文字列中の最後の%
記号を探し、それより前をホスト、後をゾーンとして分離します。
また、IPAddr
, TCPAddr
, UDPAddr
といったアドレス構造体にZone
フィールドが追加されたことで、パースされたゾーン情報がGoの型システム内で保持されるようになりました。
type IPAddr struct {
IP IP
Zone string // IPv6 scoped addressing zone
}
これらのアドレス構造体のString()
メソッドも更新され、Zone
フィールドに値がある場合は、IPアドレス%ゾーン識別子
の形式で文字列を生成するようになりました。これにより、Goアプリケーション内でIPv6スコープ付きアドレスが人間にも機械にも分かりやすい形式で表現されます。
例えば、(*IPAddr).String()
メソッドの変更は以下のようになります。
func (a *IPAddr) String() string {
if a == nil {
return "<nil>"
}
if a.Zone != "" {
return a.IP.String() + "%" + a.Zone
}
return a.IP.String()
}
さらに、JoinHostPort
関数は、ホスト名にコロン(IPv6リテラルアドレスの場合)またはパーセント記号(ゾーン識別子の場合)が含まれる場合に、ホスト部分を角括弧で囲むというRFCの推奨事項に従うように変更されました。これにより、[fe80::1%lo0]:80
のような形式が正しく生成・パースされるようになります。
これらの変更は、GoのネットワークプログラミングにおいてIPv6のリンクローカルアドレスを扱う際の堅牢性と利便性を大幅に向上させ、RFC 4007に準拠した実装を提供します。
関連リンク
- Go Issue #4234: net: support IPv6 scoped addressing zone
- Go Issue #4501: net: Dial/Listen should support IPv6 scoped addressing zone
- Go Issue #5081: net: IPAddr.String should return IPv6 literal address with zone identifier
- Go CL 6816116: net: support IPv6 scoped addressing zone
参考にした情報源リンク
- RFC 4007: IPv6 Scoped Address Architecture
- RFC 6874: Representing IPv6 Zone Identifiers in Literal IPv6 Addresses (RFC 4007のゾーン識別子の表現をさらに詳細化したもの)
- IPv6 Scoped Address Architecture Explained: https://www.ietf.org/proceedings/80/slides/v6ops-1.pdf
- IPv6 Link-Local Addresses and Zone IDs: https://networkengineering.stackexchange.com/questions/1000/what-are-ipv6-link-local-addresses-and-zone-ids