[インデックス 14958] ファイルの概要
このコミットは、Go言語の net パッケージにおけるNetBSD固有のネットワークインターフェースアドレス処理に関するバグ修正です。具体的には、NetBSD環境でインターフェースアドレスを取得する際に、sockaddr_dl 型のアドレスが誤って nil ではないインターフェースアドレスとして返される問題を修正し、テストコードもそれに対応して更新しています。これにより、NetBSD上でのネットワークインターフェース情報の取得がより正確になります。
コミット
- コミットハッシュ:
12e7397ebb4e8c5444f9b30b84a6b80059d073fb - 作者: Mikio Hara mikioh.mikioh@gmail.com
- コミット日時: 2013年1月23日(水) 07:11:22 +0900
- コミットメッセージ:
net: don't return nil interface address on netbsd On NetBSD routing sockaddrs for interface address contain sockaddr_dl. R=dave, rsc CC=golang-dev https://golang.org/cl/7085064
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/12e7397ebb4e8c5444f9b30b84a6b80059d073fb
元コミット内容
net: don't return nil interface address on netbsd
On NetBSD routing sockaddrs for interface address contain sockaddr_dl.
R=dave, rsc
CC=golang-dev
https://golang.org/cl/7085064
変更の背景
この変更の背景には、NetBSDオペレーティングシステムにおけるネットワークインターフェースアドレスの取得方法の特殊性があります。Unix系システムでは、ネットワークインターフェースに関する情報は通常、ioctl システムコールやルーティングソケットを通じて取得されます。これらのシステムコールは、インターフェースに割り当てられたIPアドレス、ネットマスク、ブロードキャストアドレス、マルチキャストアドレスなどの情報を提供します。
Go言語の net パッケージは、これらのOS固有のメカニズムを抽象化し、クロスプラットフォームでネットワークインターフェース情報を取得できるAPIを提供しています。しかし、NetBSDでは、ルーティングソケットから取得されるインターフェースアドレス情報の中に、sockaddr_dl (datalink socket address) 型のアドレスが含まれることがあります。この sockaddr_dl は、IPアドレスのようなネットワーク層のアドレスではなく、MACアドレスなどのデータリンク層のアドレス情報を含んでいます。
Goの net パッケージの既存の実装では、sockaddr_dl 型のアドレスが渡された場合に、それが有効なIPアドレスとして解釈されず、結果的に nil のインターフェースアドレスが生成されてしまう問題がありました。この nil のアドレスがインターフェースアドレスのリストに追加されてしまうと、後続の処理で nil ポインタ参照などの問題を引き起こす可能性がありました。
このコミットは、NetBSD環境で sockaddr_dl 型のアドレスが検出された場合に、それを有効なIPアドレスとして扱わず、nil を返すように newAddr 関数を修正することで、この問題を解決しています。これにより、interfaceAddrTable 関数が nil のインターフェースアドレスをリストに追加することを防ぎ、NetBSD上での net パッケージの堅牢性と正確性を向上させています。
前提知識の解説
このコミットを理解するためには、以下の前提知識が必要です。
-
ネットワークインターフェース (Network Interface): コンピュータがネットワークに接続するためのハードウェアまたは仮想的なコンポーネントです。各インターフェースには、IPアドレス、MACアドレス、ネットマスクなどの情報が割り当てられます。
-
ルーティングソケット (Routing Socket): Unix系OS(特にBSD系)で、カーネルのルーティングテーブルやネットワークインターフェースの状態に関する情報を取得・設定するために使用される特殊なソケットです。アプリケーションはルーティングソケットを通じて、インターフェースの追加・削除、IPアドレスの割り当て、ルーティングエントリの変更などを行うことができます。
-
sockaddr構造体: ソケットプログラミングにおいて、ソケットアドレスを表現するための汎用的な構造体です。異なる種類のアドレス(IPv4、IPv6、Unixドメインなど)を扱うために、sockaddr_in、sockaddr_in6、sockaddr_unなどの派生構造体が存在します。 -
sockaddr_dl(Datalink Socket Address): BSD系OSで定義されているsockaddrの一種で、データリンク層のアドレス(例: MACアドレス)やインターフェース名などの情報を含みます。ルーティングソケットからインターフェース情報を取得する際に、この型の情報が返されることがあります。IPアドレスのようなネットワーク層のアドレスとは異なります。 -
Go言語の
netパッケージ: Go言語の標準ライブラリの一部で、ネットワークI/O機能を提供します。TCP/IP、UDP、DNSルックアップ、ネットワークインターフェース情報の取得など、様々なネットワーク関連の操作をサポートします。 -
syscallパッケージ: Go言語の標準ライブラリの一部で、OS固有のシステムコールへの低レベルなアクセスを提供します。ネットワークインターフェース情報の取得など、OSカーネルと直接やり取りする必要がある場合に利用されます。このコミットでは、syscall.InterfaceAddrMessageやsyscall.SockaddrDatalinkといったOS固有の構造体や定数が使用されています。 -
Addrインターフェース (Go言語):netパッケージで定義されているインターフェースで、ネットワークアドレスを表します。IPAddrやIPNetなど、具体的なアドレス型がこのインターフェースを実装します。 -
interfaceAddrTable関数: Goのnetパッケージ内で、特定のインターフェースインデックスに対応するインターフェースアドレスのリストを取得する内部関数です。OS固有の実装(例:interface_bsd.go)を持ちます。 -
newAddr関数: Goのnetパッケージ内で、OSから取得した生のアドレス情報(syscall.InterfaceAddrMessage)をGoのnet.Addrインターフェース型に変換する内部関数です。この関数が、OS固有のアドレス型をGoの抽象的なアドレス型にマッピングする役割を担います。
技術的詳細
このコミットは、Go言語の src/pkg/net/interface_bsd.go ファイルと src/pkg/net/interface_test.go ファイルに変更を加えています。
interface_bsd.go は、NetBSDを含むBSD系OSにおけるネットワークインターフェース情報の取得ロジックを実装しています。このファイルには、ルーティングソケットからインターフェースアドレスメッセージを読み取り、それをGoの net.Addr 型に変換する関数が含まれています。
変更の核心は、newAddr 関数にあります。この関数は、syscall.InterfaceAddrMessage からアドレス情報を抽出し、それがIPアドレス(IPv4またはIPv6)である場合に net.IPAddr または net.IPNet 型の Addr を返します。しかし、NetBSDでは、ルーティングソケットが sockaddr_dl 型のアドレスを返すことがあり、これがIPアドレスではないにもかかわらず、以前の実装では適切に処理されていませんでした。
修正前は、newAddr 関数内で syscall.SockaddrDatalink 型のアドレスが渡された場合、それがどの case にもマッチせず、結果的に ifa (インターフェースアドレス) が初期値のまま(nil)返される可能性がありました。そして、この nil が interfaceAddrTable 関数で ifat (インターフェースアドレステーブル) に追加されていました。
今回の修正では、newAddr 関数に default ケースを追加し、syscall.SockaddrDatalink 型のアドレスが渡された場合に明示的に nil, nil (アドレスとエラーの両方が nil) を返すようにしました。これにより、interfaceAddrTable 関数は nil のアドレスを受け取った際に、それを ifat に追加しないように変更されています。
また、テストファイル interface_test.go も更新され、testAddrs および testMulticastAddrs 関数内で、取得したインターフェースアドレスが nil でないことを確認するチェックが追加されました。これにより、将来的に同様の問題が発生した場合にテストで検出できるようになります。
コアとなるコードの変更箇所
diff --git a/src/pkg/net/interface_bsd.go b/src/pkg/net/interface_bsd.go
index 7f090d8d40..df9b3a2f27 100644
--- a/src/pkg/net/interface_bsd.go
+++ b/src/pkg/net/interface_bsd.go
@@ -118,7 +118,9 @@ func interfaceAddrTable(ifindex int) ([]Addr, error) {
if err != nil {
return nil, err
}
- ifat = append(ifat, ifa)
+ if ifa != nil {
+ ifat = append(ifat, ifa)
+ }
}
}
return ifa, nil
@@ -157,6 +159,8 @@ func newAddr(m *syscall.InterfaceAddrMessage) (Addr, error) {
ifa.IP[2], ifa.IP[3] = 0, 0
}
}
+ default: // Sockaddrs contain syscall.SockaddrDatalink on NetBSD
+ return nil, nil
}
}
return ifa, nil
diff --git a/src/pkg/net/interface_test.go b/src/pkg/net/interface_test.go
index 2fe0f60cae..803c1f4495 100644
--- a/src/pkg/net/interface_test.go
+++ b/src/pkg/net/interface_test.go
@@ -75,9 +75,13 @@ func testInterfaceMulticastAddrs(t *testing.T, ifi *Interface) {
func testAddrs(t *testing.T, ifat []Addr) {
for _, ifa := range ifat {
- switch ifa.(type) {
+ switch v := ifa.(type) {
case *IPAddr, *IPNet:
- t.Logf(" interface address %q", ifa.String())
+ if v == nil {
+ t.Errorf(" unexpected value: %v", ifa)
+ } else {
+ t.Logf(" interface address %q", ifa.String())
+ }
default:
t.Errorf(" unexpected type: %T", ifa)
}
@@ -86,9 +90,13 @@ func testMulticastAddrs(t *T, ifmat []Addr) {
func testMulticastAddrs(t *T, ifmat []Addr) {
for _, ifma := range ifmat {
- switch ifma.(type) {
+ switch v := ifma.(type) {
case *IPAddr:
- t.Logf(" joined group address %q", ifma.String())
+ if v == nil {
+ t.Errorf(" unexpected value: %v", ifma)
+ } else {
+ t.Logf(" joined group address %q", ifma.String())
+ }
default:
t.Errorf(" unexpected type: %T", ifma)
}
コアとなるコードの解説
src/pkg/net/interface_bsd.go の変更
-
func interfaceAddrTable(ifindex int) ([]Addr, error)内の変更:@@ -118,7 +118,9 @@ func interfaceAddrTable(ifindex int) ([]Addr, error) { if err != nil { return nil, err } - ifat = append(ifat, ifa) + if ifa != nil { + ifat = append(ifat, ifa) + } } } return ifa, nilこの変更は、
newAddr関数から返されたifa(インターフェースアドレス) がnilでない場合にのみ、ifat(インターフェースアドレステーブル) に追加するように修正しています。これにより、newAddrがsockaddr_dlのようなIPアドレスではない情報をnilとして返した場合に、そのnilがリストに含まれることを防ぎます。 -
func newAddr(m *syscall.InterfaceAddrMessage) (Addr, error)内の変更:@@ -157,6 +159,8 @@ func newAddr(m *syscall.InterfaceAddrMessage) (Addr, error) { ifa.IP[2], ifa.IP[3] = 0, 0 } } + default: // Sockaddrs contain syscall.SockaddrDatalink on NetBSD + return nil, nil } } return ifa, nilこの変更は、
newAddr関数にdefaultケースを追加しています。これは、syscall.InterfaceAddrMessage内のアドレスタイプが、既存のcase(IPv4やIPv6) のいずれにもマッチしない場合に実行されます。NetBSDでは、ルーティングソケットがsyscall.SockaddrDatalink型のアドレスを返すことがあり、これがこのdefaultケースに該当します。このdefaultケースでは、明示的にnil, nilを返すことで、データリンク層のアドレスをIPアドレスとして誤って解釈することを防ぎます。
src/pkg/net/interface_test.go の変更
-
func testAddrs(t *testing.T, ifat []Addr)内の変更:@@ -75,9 +75,13 @@ func testInterfaceMulticastAddrs(t *T, ifi *Interface) { func testAddrs(t *T, ifat []Addr) { for _, ifa := range ifat { - switch ifa.(type) { + switch v := ifa.(type) { case *IPAddr, *IPNet: - t.Logf(" interface address %q", ifa.String()) + if v == nil { + t.Errorf(" unexpected value: %v", ifa) + } else { + t.Logf(" interface address %q", ifa.String()) + } default: t.Errorf(" unexpected type: %T", ifa) }この変更は、
switchステートメントで型アサーションを行う際に、変数vを導入し、*IPAddrまたは*IPNetのケースでvがnilでないことを明示的にチェックしています。もしvがnilであれば、それは予期せぬ値であるとしてエラーを報告します。これにより、nilのインターフェースアドレスがテストを通過してしまうことを防ぎます。 -
func testMulticastAddrs(t *testing.T, ifmat []Addr)内の変更:@@ -86,9 +90,13 @@ func testMulticastAddrs(t *T, ifmat []Addr) { func testMulticastAddrs(t *T, ifmat []Addr) { for _, ifma := range ifmat { - switch ifma.(type) { + switch v := ifma.(type) { case *IPAddr: - t.Logf(" joined group address %q", ifma.String()) + if v == nil { + t.Errorf(" unexpected value: %v", ifma) + } else { + t.Logf(" joined group address %q", ifma.String()) + } default: t.Errorf(" unexpected type: %T", ifma) }testAddrsと同様に、マルチキャストアドレスのテストでもnilチェックが追加されています。これにより、マルチキャストアドレスの取得においてもnilが誤って処理されることを防ぎます。
これらの変更により、NetBSD環境でのGoの net パッケージが、データリンク層のアドレスをIPアドレスとして誤って解釈し、nil のインターフェースアドレスを生成する問題を効果的に解決しています。
関連リンク
- GitHub上のコミットページ: https://github.com/golang/go/commit/12e7397ebb4e8c5444f9b30b84a6b80059d073fb
- Go CL (Code Review): https://golang.org/cl/7085064
参考にした情報源リンク
- Go言語の
netパッケージに関する公式ドキュメント - NetBSDのルーティングソケットと
sockaddr_dlに関するドキュメント (例:rt_msg(3)manページ) - Unix系OSにおけるネットワークプログラミングに関する一般的な情報源