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

[インデックス 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.Dialnet.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アドレスのパース、表現、およびネットワーク操作にゾーン識別子のサポートを統合しています。

主な変更点は以下の通りです。

  1. 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
    
  2. parseIPv6関数の変更: IPv6アドレス文字列をパースするparseIPv6関数が、ゾーン識別子を処理できるように変更されました。この関数は、アドレスとゾーンの両方を返すようになりました。

    // func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string)
    

    zoneAllowed引数が追加され、ゾーン識別子のパースを制御します。

  3. splitHostZone関数の追加: ホスト文字列からゾーン識別子を分離するための新しいヘルパー関数splitHostZoneが追加されました。

    // func splitHostZone(s string) (host, zone string)
    
  4. 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
    // }
    
  5. SplitHostPort関数の変更: アドレス文字列をホストとポートに分割するSplitHostPort関数が、[ipv6-host%zone]:port形式を認識し、ゾーン識別子を適切に処理するように変更されました。

  6. Dial, Listen, ListenPacket関数の変更: これらのネットワーク接続確立関数が、引数として渡されるアドレス文字列にゾーン識別子が含まれている場合、それを適切にパースして利用するように内部的に変更されました。

  7. ResolveIPAddr, ResolveTCPAddr, ResolveUDPAddr関数の変更: これらのアドレス解決関数が、ゾーン識別子を含むアドレス文字列を解決し、結果のIPAddr, TCPAddr, UDPAddr構造体にゾーン情報を設定するように変更されました。

  8. 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に準拠した実装を提供します。

関連リンク

参考にした情報源リンク