[インデックス 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におけるネットワークプログラミングに関する一般的な情報源