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

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

このコミットは、Go言語の標準ライブラリであるnetおよびsyscallパッケージにおける、FreeBSD環境でのIPv6ネットワークマスクの取り扱いに関するバグ修正を目的としています。具体的には、FreeBSDの最新バージョン(特にFreeBSD 9.1)において、ルーティングテーブルメッセージ(RTM_GETなどの応答)から取得されるネットワークマスク情報にアドレスファミリー識別子(AF_UNSPEC)が正しく設定されない問題に対応しています。

コミット

commit 262d6f58c78d53d2e85957075de0f817861cc23c
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Wed Aug 7 00:25:23 2013 +0900

    syscall: fix IPv6 wrong network mask on latest FreeBSD
    
    Looks like latest FreeBSD doesn't set address family identifer
    for RTAX_NETMASK stuff; probably RTAX_GENMASK too, not confirmed.
    This CL tries to identify address families by using the length of
    each socket address if possible.
    
    The issue is confirmed on FreeBSD 9.1.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/12332043

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

https://github.com/golang/go/commit/262d6f58c78d53d2e85957075de0f817861cc23c

元コミット内容

このコミットの元の内容は、FreeBSDの最新バージョン(特に9.1)において、ルーティングテーブルメッセージ内のRTAX_NETMASK(およびおそらくRTAX_GENMASK)フィールドにアドレスファミリー識別子(AF_UNSPEC)が設定されない問題を修正することです。この問題により、Goのsyscallパッケージがネットワークマスクを正しく解釈できず、結果としてIPv6アドレスのネットワークマスクが誤って認識される可能性がありました。

この修正は、ソケットアドレスの長さ(rsa.Len)に基づいてアドレスファミリーを推測することで、この問題を回避しようとします。具体的には、SizeofSockaddrInet4(IPv4ソケットアドレスのサイズ)であればAF_INETSizeofSockaddrInet6(IPv6ソケットアドレスのサイズ)であればAF_INET6と判断し、それ以外の場合は従来のAF_INETと見なすように変更されています。

また、net/interface_test.goには、IPNet型のアドレスが正しく処理されているかを確認するためのテストケースが追加されています。これは、IPv4およびIPv6アドレスのプレフィックス長がそれぞれ8*IPv4lenおよび8*IPv6lenと一致するかを検証するものです。

変更の背景

この変更の背景には、FreeBSDのルーティングテーブルメッセージの構造に関する特定の挙動の変化があります。従来のFreeBSDでは、ルーティングテーブルメッセージ内のソケットアドレス構造体(sockaddr)において、RTAX_NETMASKフィールドにネットワークマスクが格納される際に、そのアドレスファミリー(IPv4かIPv6か)を示す識別子(sa_family)が適切に設定されていました。しかし、FreeBSD 9.1以降のバージョンでは、このsa_familyフィールドがAF_UNSPEC(未指定)として返されるようになりました。

Goのsyscallパッケージは、これらのルーティングテーブルメッセージをパースしてネットワークインターフェース情報を取得します。AF_UNSPECが設定されている場合、GoのコードはデフォルトでIPv4(AF_INET)と解釈していました。この挙動はIPv4アドレスのネットワークマスクには問題ありませんでしたが、IPv6アドレスのネットワークマスクに対しては誤った解釈を引き起こし、結果としてIPv6のネットワーク設定が正しく行われない、あるいは誤った情報が取得されるというバグが発生していました。

このコミットは、このFreeBSDの挙動変更に対応し、AF_UNSPECが設定されている場合でも、ソケットアドレスの実際の長さからアドレスファミリーを推測することで、IPv6ネットワークマスクを正しく識別できるようにするためのものです。

前提知識の解説

このコミットを理解するためには、以下の概念について理解しておく必要があります。

1. ルーティングテーブルとルーティングテーブルメッセージ (RTM)

オペレーティングシステムは、ネットワークトラフィックをどこに転送するかを決定するために「ルーティングテーブル」を保持しています。このテーブルには、宛先ネットワーク、ネクストホップルーター、使用するネットワークインターフェースなどの情報が含まれています。

FreeBSDのようなBSD系のシステムでは、カーネルとユーザーランドのプロセス間でルーティング情報をやり取りするために「ルーティングソケット」と「ルーティングテーブルメッセージ (RTM)」が使用されます。ユーザーランドのプログラムはルーティングソケットを通じてカーネルにルーティング情報の変更を要求したり、カーネルからルーティングイベント(例: ルートの追加/削除、インターフェースの状態変更)の通知を受け取ったりします。

RTMは、ルーティング情報をカプセル化するための構造化されたメッセージです。これらのメッセージは、rt_msghdrというヘッダと、それに続く複数のソケットアドレス構造体(sockaddr)で構成されます。

2. sockaddr 構造体とアドレスファミリー

sockaddrは、ソケット通信においてアドレス情報を表現するための汎用的な構造体です。様々な種類のアドレス(IPv4、IPv6、Unixドメインなど)を扱うために、sockaddrの後に特定のアドレスファミリーに応じた具体的な構造体(例: sockaddr_insockaddr_in6)が続きます。

sockaddr構造体には、sa_familyというフィールドがあり、これはアドレスの種類(アドレスファミリー)を示します。一般的なアドレスファミリーには以下のようなものがあります。

  • AF_INET: IPv4アドレス
  • AF_INET6: IPv6アドレス
  • AF_UNSPEC: 未指定、または不明なアドレスファミリー

3. RTAX_NETMASKRTAX_GENMASK

RTMのメッセージボディには、ルーティングエントリの様々な属性が格納されます。これらの属性は、rt_msghdrrtm_addrsフィールドによってビットマスク形式で示され、それぞれのビットが特定のソケットアドレス構造体の存在を示します。

  • RTAX_NETMASK: ネットワークマスク(サブネットマスク)を示すソケットアドレスがメッセージに含まれていることを示します。
  • RTAX_GENMASK: 一般的なマスク(例: ルートのスコープマスク)を示すソケットアドレスがメッセージに含まれていることを示します。

これらのフィールドに対応するsockaddr構造体は、ルーティングテーブルエントリのネットワークアドレスやマスク情報を提供します。

4. Go言語の net および syscall パッケージ

  • netパッケージ: Go言語の標準ネットワークインターフェースを提供するパッケージです。IPアドレス、ネットワークインターフェース、TCP/UDP接続などを抽象化して扱います。net.IPAddrはIPアドレスを、net.IPNetはIPアドレスとネットワークマスクの組(CIDR表記など)を表します。
  • syscallパッケージ: オペレーティングシステムの低レベルなシステムコールにアクセスするためのパッケージです。このコミットでは、FreeBSDカーネルからルーティング情報を取得するために、syscallパッケージがルーティングソケットからのメッセージをパースする部分が関係しています。

5. IPAddr, IPNet, IPv4len, IPv6len

  • net.IPAddr: 単一のIPアドレスを表す型。
  • net.IPNet: IPネットワークを表す型。IPアドレスとネットワークマスク(IPMask)のペアで構成されます。IPMaskはビットマスクとして表現され、Size()メソッドでプレフィックス長(例: /24の24)を取得できます。
  • net.IPv4len: IPv4アドレスのバイト長(4バイト)。
  • net.IPv6len: IPv6アドレスのバイト長(16バイト)。

6. SizeofSockaddrInet4SizeofSockaddrInet6

これらは、それぞれIPv4ソケットアドレス(sockaddr_in)とIPv6ソケットアドレス(sockaddr_in6)の構造体のバイトサイズを表す定数です。これらのサイズは、特定のアドレスファミリーのソケットアドレスがメモリ上でどれくらいの領域を占めるかを決定します。

技術的詳細

このコミットの技術的な核心は、FreeBSDのルーティングテーブルメッセージのパースロジックにおけるAF_UNSPECの取り扱いを改善することにあります。

FreeBSD 9.1以降では、RTAX_NETMASKフィールドに対応するsockaddr構造体のsa_familyAF_UNSPECとして返されるようになりました。これは、カーネルがネットワークマスクのアドレスファミリーを明示的に設定しないという最適化、あるいは変更によるものと考えられます。Goの既存のsyscallパッケージでは、RTAX_NETMASKsa_familyAF_UNSPECの場合、歴史的な理由からデフォルトでAF_INET(IPv4)と解釈していました。

このデフォルトの解釈は、IPv4のネットワークマスクには問題ありませんが、IPv6のネットワークマスクに対しては誤ったプレフィックス長を導き出す原因となります。例えば、IPv6アドレスのネットワークマスクがAF_UNSPECとして渡された場合、GoのコードはそれをIPv4のマスクとして処理しようとし、結果として不正確なネットワーク情報が生成されていました。

このコミットでは、src/pkg/syscall/route_bsd.go内のInterfaceAddrMessage.sockaddr()メソッドに修正が加えられました。このメソッドは、ルーティングテーブルメッセージからソケットアドレスを抽出し、GoのSockaddr型に変換する役割を担っています。

修正前は、RTAX_NETMASKrsa.FamilyAF_UNSPECの場合、無条件にrsa.Family = AF_INETと設定していました。 修正後は、AF_UNSPECの場合に、rsa.Len(ソケットアドレスのバイト長)をチェックするswitch文が導入されました。

  • rsa.LenSizeofSockaddrInet4(IPv4ソケットアドレスのサイズ)と一致する場合、rsa.FamilyAF_INETに設定します。
  • rsa.LenSizeofSockaddrInet6(IPv6ソケットアドレスのサイズ)と一致する場合、rsa.FamilyAF_INET6に設定します。
  • 上記以外の場合(不明なサイズ、または従来の挙動を維持する場合)、rsa.FamilyAF_INETに設定します。

この変更により、AF_UNSPECが設定されたネットワークマスクであっても、そのバイト長から正確なアドレスファミリーを推測し、IPv6のネットワークマスクを正しく処理できるようになりました。

また、src/pkg/net/interface_test.goには、IPNet型のアドレスが正しくパースされているかを検証するためのテストロジックが追加されました。これは、IPNetIPがIPv4であればプレフィックス長が8*IPv4len(32ビット)であること、IPv6であればプレフィックス長が8*IPv6len(128ビット)であることを確認するものです。これにより、ネットワークマスクの解釈が正しく行われていることを保証します。

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

src/pkg/net/interface_test.go

--- a/src/pkg/net/interface_test.go
+++ b/src/pkg/net/interface_test.go
@@ -108,12 +108,23 @@ func testInterfaceMulticastAddrs(t *testing.T, ifi *Interface) {
 func testAddrs(t *testing.T, ifat []Addr) {
 	for _, ifa := range ifat {
 		switch ifa := ifa.(type) {
-		case *IPAddr, *IPNet:
+		case *IPAddr:
 			if ifa == nil {
 				t.Errorf("\tunexpected value: %v", ifa)
 			} else {
 				t.Logf("\tinterface address %q", ifa.String())
 			}
+		case *IPNet:
+			if ifa == nil {
+				t.Errorf("\tunexpected value: %v", ifa)
+			} else {
+				_, prefixLen := ifa.Mask.Size()
+				if ifa.IP.To4() != nil && prefixLen != 8*IPv4len || ifa.IP.To16() != nil && ifa.IP.To4() == nil && prefixLen != 8*IPv6len {
+					t.Errorf("\tunexpected value: %v, %v, %v, %v", ifa, ifa.IP, ifa.Mask, prefixLen)
+				} else {
+					t.Logf("\tinterface address %q", ifa.String())
+				}
+			}
 		default:
 			t.Errorf("\tunexpected type: %T", ifa)
 		}

src/pkg/syscall/route_bsd.go

--- a/src/pkg/syscall/route_bsd.go
+++ b/src/pkg/syscall/route_bsd.go
@@ -156,7 +156,14 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
 		case RTAX_NETMASK:
 			if rsa.Family == AF_UNSPEC {
-				rsa.Family = AF_INET // an old fasion, AF_UNSPEC means AF_INET
+				switch rsa.Len {
+				case SizeofSockaddrInet4:
+					rsa.Family = AF_INET
+				case SizeofSockaddrInet6:
+					rsa.Family = AF_INET6
+				default:
+					rsa.Family = AF_INET // an old fasion, AF_UNSPEC means AF_INET
+				}
 			}
 			sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
 			if err != nil {

コアとなるコードの解説

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

このファイルでは、testAddrs関数内のswitch文が変更されています。

  • 変更前は、*IPAddr*IPNetの両方をまとめてcase *IPAddr, *IPNet:で処理していました。
  • 変更後は、*IPAddr*IPNetを個別のcaseで処理するように分離されました。
  • 特にcase *IPNet:ブロックが追加され、IPNet型のアドレスに対する詳細な検証ロジックが導入されました。
    • ifa.Mask.Size()を呼び出してプレフィックス長を取得します。
    • ifa.IP.To4() != nil && prefixLen != 8*IPv4len:もしIPアドレスがIPv4であり、かつプレフィックス長がIPv4のビット長(32ビット)と異なる場合、エラーを報告します。
    • ifa.IP.To16() != nil && ifa.IP.To4() == nil && prefixLen != 8*IPv6len:もしIPアドレスがIPv6であり(かつIPv4ではない)、かつプレフィックス長がIPv6のビット長(128ビット)と異なる場合、エラーを報告します。
    • このテストは、syscallパッケージがルーティングメッセージから取得したIPNet情報が、IPアドレスのバージョンに応じた正しいプレフィックス長を持っていることを保証します。これにより、ネットワークマスクのパースが正しく行われているかを確認できます。

src/pkg/syscall/route_bsd.go の変更

このファイルでは、InterfaceAddrMessage構造体のsockaddr()メソッド内のcase RTAX_NETMASK:ブロックが変更されています。

  • if rsa.Family == AF_UNSPEC:ルーティングメッセージから取得したソケットアドレス(rsa)のアドレスファミリーがAF_UNSPEC(未指定)である場合に、以下のロジックが適用されます。
  • switch rsa.LenAF_UNSPECの場合、ソケットアドレスの実際のバイト長(rsa.Len)に基づいてアドレスファミリーを推測します。
    • case SizeofSockaddrInet4::もし長さがIPv4ソケットアドレスのサイズと一致すれば、rsa.FamilyAF_INET(IPv4)に設定します。
    • case SizeofSockaddrInet6::もし長さがIPv6ソケットアドレスのサイズと一致すれば、rsa.FamilyAF_INET6(IPv6)に設定します。
    • default::上記いずれにも該当しない場合、または不明な場合は、従来の挙動に従いrsa.FamilyAF_INET(IPv4)に設定します。これは、古いFreeBSDバージョンや、IPv4のネットワークマスクがAF_UNSPECで返される可能性のあるケースに対応するためです。

この変更により、FreeBSD 9.1以降で発生していたIPv6ネットワークマスクの誤認識問題が解決され、Goのnetパッケージがシステムから正確なネットワークインターフェース情報を取得できるようになります。

関連リンク

参考にした情報源リンク

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

このコミットは、Go言語の標準ライブラリであるnetおよびsyscallパッケージにおける、FreeBSD環境でのIPv6ネットワークマスクの取り扱いに関するバグ修正を目的としています。具体的には、FreeBSDの最新バージョン(特にFreeBSD 9.1)において、ルーティングテーブルメッセージ(RTM_GETなどの応答)から取得されるネットワークマスク情報にアドレスファミリー識別子(AF_UNSPEC)が正しく設定されない問題に対応しています。

コミット

commit 262d6f58c78d53d2e85957075de0f817861cc23c
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Wed Aug 7 00:25:23 2013 +0900

    syscall: fix IPv6 wrong network mask on latest FreeBSD
    
    Looks like latest FreeBSD doesn't set address family identifer
    for RTAX_NETMASK stuff; probably RTAX_GENMASK too, not confirmed.
    This CL tries to identify address families by using the length of
    each socket address if possible.
    
    The issue is confirmed on FreeBSD 9.1.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/12332043

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

https://github.com/golang/go/commit/262d6f58c78d53d2e85957075de0f817861cc23c

元コミット内容

このコミットの元の内容は、FreeBSDの最新バージョン(特に9.1)において、ルーティングテーブルメッセージ内のRTAX_NETMASK(およびおそらくRTAX_GENMASK)フィールドにアドレスファミリー識別子(AF_UNSPEC)が設定されない問題を修正することです。この問題により、Goのsyscallパッケージがネットワークマスクを正しく解釈できず、結果としてIPv6アドレスのネットワークマスクが誤って認識される可能性がありました。

この修正は、ソケットアドレスの長さ(rsa.Len)に基づいてアドレスファミリーを推測することで、この問題を回避しようとします。具体的には、SizeofSockaddrInet4(IPv4ソケットアドレスのサイズ)であればAF_INETSizeofSockaddrInet6(IPv6ソケットアドレスのサイズ)であればAF_INET6と判断し、それ以外の場合は従来のAF_INETと見なすように変更されています。

また、net/interface_test.goには、IPNet型のアドレスが正しく処理されているかを確認するためのテストケースが追加されています。これは、IPv4およびIPv6アドレスのプレフィックス長がそれぞれ8*IPv4lenおよび8*IPv6lenと一致するかを検証するものです。

変更の背景

この変更の背景には、FreeBSDのルーティングテーブルメッセージの構造に関する特定の挙動の変化があります。従来のFreeBSDでは、ルーティングテーブルメッセージ内のソケットアドレス構造体(sockaddr)において、RTAX_NETMASKフィールドにネットワークマスクが格納される際に、そのアドレスファミリー(IPv4かIPv6か)を示す識別子(sa_family)が適切に設定されていました。しかし、FreeBSD 9.1以降のバージョンでは、このsa_familyフィールドがAF_UNSPEC(未指定)として返されるようになりました。

Goのsyscallパッケージは、これらのルーティングテーブルメッセージをパースしてネットワークインターフェース情報を取得します。AF_UNSPECが設定されている場合、GoのコードはデフォルトでIPv4(AF_INET)と解釈していました。この挙動はIPv4アドレスのネットワークマスクには問題ありませんでしたが、IPv6アドレスのネットワークマスクに対しては誤った解釈を引き起こし、結果としてIPv6のネットワーク設定が正しく行われない、あるいは誤った情報が取得されるというバグが発生していました。

このコミットは、このFreeBSDの挙動変更に対応し、AF_UNSPECが設定されている場合でも、ソケットアドレスの実際の長さからアドレスファミリーを推測することで、IPv6ネットワークマスクを正しく識別できるようにするためのものです。

前提知識の解説

このコミットを理解するためには、以下の概念について理解しておく必要があります。

1. ルーティングテーブルとルーティングテーブルメッセージ (RTM)

オペレーティングシステムは、ネットワークトラフィックをどこに転送するかを決定するために「ルーティングテーブル」を保持しています。このテーブルには、宛先ネットワーク、ネクストホップルーター、使用するネットワークインターフェースなどの情報が含まれています。

FreeBSDのようなBSD系のシステムでは、カーネルとユーザーランドのプロセス間でルーティング情報をやり取りするために「ルーティングソケット」と「ルーティングテーブルメッセージ (RTM)」が使用されます。ユーザーランドのプログラムはルーティングソケットを通じてカーネルにルーティング情報の変更を要求したり、カーネルからルーティングイベント(例: ルートの追加/削除、インターフェースの状態変更)の通知を受け取ったりします。

RTMは、ルーティング情報をカプセル化するための構造化されたメッセージです。これらのメッセージは、rt_msghdrというヘッダと、それに続く複数のソケットアドレス構造体(sockaddr)で構成されます。

2. sockaddr 構造体とアドレスファミリー

sockaddrは、ソケット通信においてアドレス情報を表現するための汎用的な構造体です。様々な種類のアドレス(IPv4、IPv6、Unixドメインなど)を扱うために、sockaddrの後に特定のアドレスファミリーに応じた具体的な構造体(例: sockaddr_insockaddr_in6)が続きます。

sockaddr構造体には、sa_familyというフィールドがあり、これはアドレスの種類(アドレスファミリー)を示します。一般的なアドレスファミリーには以下のようなものがあります。

  • AF_INET: IPv4アドレス
  • AF_INET6: IPv6アドレス
  • AF_UNSPEC: 未指定、または不明なアドレスファミリー

3. RTAX_NETMASKRTAX_GENMASK

RTMのメッセージボディには、ルーティングエントリの様々な属性が格納されます。これらの属性は、rt_msghdrrtm_addrsフィールドによってビットマスク形式で示され、それぞれのビットが特定のソケットアドレス構造体の存在を示します。

  • RTAX_NETMASK: ネットワークマスク(サブネットマスク)を示すソケットアドレスがメッセージに含まれていることを示します。
  • RTAX_GENMASK: 一般的なマスク(例: ルートのスコープマスク)を示すソケットアドレスがメッセージに含まれていることを示します。

これらのフィールドに対応するsockaddr構造体は、ルーティングテーブルエントリのネットワークアドレスやマスク情報を提供します。

4. Go言語の net および syscall パッケージ

  • netパッケージ: Go言語の標準ネットワークインターフェースを提供するパッケージです。IPアドレス、ネットワークインターフェース、TCP/UDP接続などを抽象化して扱います。net.IPAddrはIPアドレスを、net.IPNetはIPアドレスとネットワークマスクの組(CIDR表記など)を表します。
  • syscallパッケージ: オペレーティングシステムの低レベルなシステムコールにアクセスするためのパッケージです。このコミットでは、FreeBSDカーネルからルーティング情報を取得するために、syscallパッケージがルーティングソケットからのメッセージをパースする部分が関係しています。

5. IPAddr, IPNet, IPv4len, IPv6len

  • net.IPAddr: 単一のIPアドレスを表す型。
  • net.IPNet: IPネットワークを表す型。IPアドレスとネットワークマスク(IPMask)のペアで構成されます。IPMaskはビットマスクとして表現され、Size()メソッドでプレフィックス長(例: /24の24)を取得できます。
  • net.IPv4len: IPv4アドレスのバイト長(4バイト)。
  • net.IPv6len: IPv6アドレスのバイト長(16バイト)。

6. SizeofSockaddrInet4SizeofSockaddrInet6

これらは、それぞれIPv4ソケットアドレス(sockaddr_in)とIPv6ソケットアドレス(sockaddr_in6)の構造体のバイトサイズを表す定数です。これらのサイズは、特定のアドレスファミリーのソケットアドレスがメモリ上でどれくらいの領域を占めるかを決定します。

技術的詳細

このコミットの技術的な核心は、FreeBSDのルーティングテーブルメッセージのパースロジックにおけるAF_UNSPECの取り扱いを改善することにあります。

FreeBSD 9.1以降では、RTAX_NETMASKフィールドに対応するsockaddr構造体のsa_familyAF_UNSPECとして返されるようになりました。これは、カーネルがネットワークマスクのアドレスファミリーを明示的に設定しないという最適化、あるいは変更によるものと考えられます。Goの既存のsyscallパッケージでは、RTAX_NETMASKsa_familyAF_UNSPECの場合、歴史的な理由からデフォルトでAF_INET(IPv4)と解釈していました。

このデフォルトの解釈は、IPv4のネットワークマスクには問題ありませんが、IPv6のネットワークマスクに対しては誤ったプレフィックス長を導き出す原因となります。例えば、IPv6アドレスのネットワークマスクがAF_UNSPECとして渡された場合、GoのコードはそれをIPv4のマスクとして処理しようとし、結果として不正確なネットワーク情報が生成されていました。

このコミットでは、src/pkg/syscall/route_bsd.go内のInterfaceAddrMessage.sockaddr()メソッドに修正が加えられました。このメソッドは、ルーティングテーブルメッセージからソケットアドレスを抽出し、GoのSockaddr型に変換する役割を担っています。

修正前は、RTAX_NETMASKrsa.FamilyAF_UNSPECの場合、無条件にrsa.Family = AF_INETと設定していました。 修正後は、AF_UNSPECの場合に、rsa.Len(ソケットアドレスのバイト長)をチェックするswitch文が導入されました。

  • rsa.LenSizeofSockaddrInet4(IPv4ソケットアドレスのサイズ)と一致する場合、rsa.FamilyAF_INETに設定します。
  • rsa.LenSizeofSockaddrInet6(IPv6ソケットアドレスのサイズ)と一致する場合、rsa.FamilyAF_INET6に設定します。
  • 上記以外の場合(不明なサイズ、または従来の挙動を維持する場合)、rsa.FamilyAF_INETに設定します。

この変更により、AF_UNSPECが設定されたネットワークマスクであっても、そのバイト長から正確なアドレスファミリーを推測し、IPv6のネットワークマスクを正しく処理できるようになりました。

また、src/pkg/net/interface_test.goには、IPNet型のアドレスが正しくパースされているかを検証するためのテストロジックが追加されました。これは、IPNetIPがIPv4であればプレフィックス長が8*IPv4len(32ビット)であること、IPv6であればプレフィックス長が8*IPv6len(128ビット)であることを確認するものです。これにより、ネットワークマスクの解釈が正しく行われていることを保証します。

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

src/pkg/net/interface_test.go

--- a/src/pkg/net/interface_test.go
+++ b/src/pkg/net/interface_test.go
@@ -108,12 +108,23 @@ func testInterfaceMulticastAddrs(t *testing.T, ifi *Interface) {
 func testAddrs(t *testing.T, ifat []Addr) {
 	for _, ifa := range ifat {
 		switch ifa := ifa.(type) {
-		case *IPAddr, *IPNet:
+		case *IPAddr:
 			if ifa == nil {
 				t.Errorf("\tunexpected value: %v", ifa)
 			} else {
 				t.Logf("\tinterface address %q", ifa.String())
 			}
+		case *IPNet:
+			if ifa == nil {
+				t.Errorf("\tunexpected value: %v", ifa)
+			} else {
+				_, prefixLen := ifa.Mask.Size()
+				if ifa.IP.To4() != nil && prefixLen != 8*IPv4len || ifa.IP.To16() != nil && ifa.IP.To4() == nil && prefixLen != 8*IPv6len {
+					t.Errorf("\tunexpected value: %v, %v, %v, %v", ifa, ifa.IP, ifa.Mask, prefixLen)
+				} else {
+					t.Logf("\tinterface address %q", ifa.String())
+				}
+			}
 		default:
 			t.Errorf("\tunexpected type: %T", ifa)
 		}

src/pkg/syscall/route_bsd.go

--- a/src/pkg/syscall/route_bsd.go
+++ b/src/pkg/syscall/route_bsd.go
@@ -156,7 +156,14 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
 		case RTAX_NETMASK:
 			if rsa.Family == AF_UNSPEC {
-				rsa.Family = AF_INET // an old fasion, AF_UNSPEC means AF_INET
+				switch rsa.Len {
+				case SizeofSockaddrInet4:
+					rsa.Family = AF_INET
+				case SizeofSockaddrInet6:
+					rsa.Family = AF_INET6
+				default:
+					rsa.Family = AF_INET // an old fasion, AF_UNSPEC means AF_INET
+				}
 			}
 			sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
 			if err != nil {

コアとなるコードの解説

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

このファイルでは、testAddrs関数内のswitch文が変更されています。

  • 変更前は、*IPAddr*IPNetの両方をまとめてcase *IPAddr, *IPNet:で処理していました。
  • 変更後は、*IPAddr*IPNetを個別のcaseで処理するように分離されました。
  • 特にcase *IPNet:ブロックが追加され、IPNet型のアドレスに対する詳細な検証ロジックが導入されました。
    • ifa.Mask.Size()を呼び出してプレフィックス長を取得します。
    • ifa.IP.To4() != nil && prefixLen != 8*IPv4len:もしIPアドレスがIPv4であり、かつプレフィックス長がIPv4のビット長(32ビット)と異なる場合、エラーを報告します。
    • ifa.IP.To16() != nil && ifa.IP.To4() == nil && prefixLen != 8*IPv6len:もしIPアドレスがIPv6であり(かつIPv4ではない)、かつプレフィックス長がIPv6のビット長(128ビット)と異なる場合、エラーを報告します。
    • このテストは、syscallパッケージがルーティングメッセージから取得したIPNet情報が、IPアドレスのバージョンに応じた正しいプレフィックス長を持っていることを保証します。これにより、ネットワークマスクのパースが正しく行われているかを確認できます。

src/pkg/syscall/route_bsd.go の変更

このファイルでは、InterfaceAddrMessage構造体のsockaddr()メソッド内のcase RTAX_NETMASK:ブロックが変更されています。

  • if rsa.Family == AF_UNSPEC:ルーティングメッセージから取得したソケットアドレス(rsa)のアドレスファミリーがAF_UNSPEC(未指定)である場合に、以下のロジックが適用されます。
  • switch rsa.LenAF_UNSPECの場合、ソケットアドレスの実際のバイト長(rsa.Len)に基づいてアドレスファミリーを推測します。
    • case SizeofSockaddrInet4::もし長さがIPv4ソケットアドレスのサイズと一致すれば、rsa.FamilyAF_INET(IPv4)に設定します。
    • case SizeofSockaddrInet6::もし長さがIPv6ソケットアドレスのサイズと一致すれば、rsa.FamilyAF_INET6(IPv6)に設定します。
    • default::上記いずれにも該当しない場合、または不明な場合は、従来の挙動に従いrsa.FamilyAF_INET(IPv4)に設定します。これは、古いFreeBSDバージョンや、IPv4のネットワークマスクがAF_UNSPECで返される可能性のあるケースに対応するためです。

この変更により、FreeBSD 9.1以降で発生していたIPv6ネットワークマスクの誤認識問題が解決され、Goのnetパッケージがシステムから正確なネットワークインターフェース情報を取得できるようになります。

関連リンク

参考にした情報源リンク