[インデックス 18719] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおけるFreeBSD関連のルーティングメッセージ処理を更新し、FreeBSD 10以降のバージョンに対応するための変更を導入しています。具体的には、src/pkg/syscall/route_freebsd.go
、src/pkg/syscall/route_freebsd_32bit.go
、src/pkg/syscall/route_freebsd_64bit.go
の3つのファイルが変更されています。
コミット
FreeBSD 10におけるInterfaceMessage
の構造変更に対応するため、syscall
パッケージを修正しました。これにより、FreeBSD 10およびそれ以前のバージョンでsyscall.InterfaceMessage
が正しく機能するようになります。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/25668b9b349cf076f3ac07c02556a77d169f897b
元コミット内容
commit 25668b9b349cf076f3ac07c02556a77d169f897b
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Tue Mar 4 09:26:56 2014 +0900
syscall: add support for FreeBSD 10
This CL tweaks syscall.InterfaceMessage to support FreeBSD 10 and
prior to 10.
See http://svnweb.freebsd.org/base?view=revision&revision=254804.
Fixes #7193.
LGTM=iant
R=golang-codereviews, rsc, minux.ma, gobot, iant
CC=golang-codereviews
https://golang.org/cl/56980043
変更の背景
この変更の背景には、FreeBSD 10におけるネットワーク関連の内部データ構造、特にmbuf
(memory buffer)の再構築があります。FreeBSDのネットワークスタックは、パケットやソケットバッファを効率的に管理するためにmbuf
を使用しています。FreeBSD 10では、このmbuf
のパケットヘッダビューが変更され、これまでのバージョンとは異なる構造になりました。
具体的には、FreeBSDのSVNリビジョン254804(2013年8月22日)を含む一連の変更がmbuf
構造に適用されました。これにより、if_msghdr
構造体(ネットワークインターフェースメッセージのヘッダ)の内部レイアウトが変更され、特にif_data
フィールド内のHwassist
、Epoch
、Lastchange
といったフィールドのオフセットやサイズが影響を受けました。
Go言語のsyscall
パッケージは、オペレーティングシステムの低レベルな機能にアクセスするためのインターフェースを提供しており、FreeBSDのルーティングメッセージ(RTM_IFINFO
など)を処理する際に、これらのカーネル内部構造を直接利用していました。FreeBSD 10でのmbuf
構造の変更により、Goのsyscall.InterfaceMessage
がFreeBSD 10で正しくパースできなくなる問題が発生しました(Go issue #7193)。
このコミットは、FreeBSDのバージョンを検出し、バージョン10以降であれば新しいmbuf
構造に対応したパースロジックを適用することで、この互換性の問題を解決することを目的としています。
前提知識の解説
Go言語のsyscall
パッケージ
syscall
パッケージは、Goプログラムからオペレーティングシステムの低レベルなプリミティブ(システムコール)にアクセスするための機能を提供します。これにより、プロセス管理、ファイル操作、ネットワーク通信などを非常に基本的なレベルで制御できます。しかし、このパッケージはGo 1.4以降「ロックダウン」されており、新しいコードではgolang.org/x/sys
やgolang.org/x/net
といった外部パッケージの使用が推奨されています。
syscall.InterfaceMessage
syscall.InterfaceMessage
は、syscall
パッケージ内で定義されているGoの構造体で、ネットワークインターフェースのエントリを含むルーティングメッセージを表します。これは通常、ルーティングに関連する低レベルなネットワーク操作の一部として使用されます。Header
フィールド(IfMsghdr
型)にはメッセージのメタデータが含まれ、Data
フィールド([]byte
型)にはネットワークインターフェースに関する実際のデータが格納されます。
mbuf
(memory buffer)
mbuf
は、FreeBSDを含む多くのUnix系OSのネットワークスタックにおいて、ネットワークパケットやソケットバッファを効率的に管理するためのメモリバッファ構造です。ネットワークデータを受信・送信する際に、カーネル内でmbuf
が割り当てられ、パケットデータが格納されます。mbuf
の構造は、パフォーマンスやメモリ効率に直結するため、OSのバージョンアップに伴って変更されることがあります。
sysctl
とkern.osreldate
sysctl
は、FreeBSD(および他のUnix系OS)において、カーネルパラメータをランタイムで検査・変更するためのメカニズムです。これらのパラメータは、MIB(Management Information Base)形式の名前階層で整理されています。
kern.osreldate
は、FreeBSDのsysctl
変数の一つで、オペレーティングシステムのリリース日を表す読み取り専用の整数値です。この値は通常、<osreldate.h>
ヘッダファイルで定義されている__FreeBSD_version
マクロに対応しており、システムがビルドされたユーザーランドのバージョンを示します。この値を利用することで、Goプログラムは実行中のFreeBSDのバージョンを判別し、それに応じた処理を行うことができます。FreeBSD 10のkern.osreldate
の値は1000000
以上となります。
技術的詳細
このコミットの主要な技術的アプローチは、FreeBSDのバージョンを動的に検出し、それに基づいてInterfaceMessage
のパースロジックを切り替えることです。
-
FreeBSDバージョンの検出:
src/pkg/syscall/route_freebsd.go
にfreebsdVersion
というグローバル変数が追加され、init()
関数内でSysctlUint32("kern.osreldate")
を呼び出すことで、システムのkern.osreldate
の値を取得します。この値は、FreeBSDのバージョンを数値で表したもので、FreeBSD 10以降では1000000
以上となります。 -
InterfaceMessage
のパースロジックの分離: 既存のanyMessage.toRoutingMessage
関数内でRTM_IFINFO
メッセージを処理する部分が変更されました。以前は直接InterfaceMessage
にキャストしていましたが、新しいany.parseInterfaceMessage(b)
メソッドを呼び出すように変更されました。 -
アーキテクチャごとのパースロジック:
parseInterfaceMessage
メソッドの実装は、アーキテクチャ(32bitと64bit)ごとに異なるファイルに分離されました。src/pkg/syscall/route_freebsd_32bit.go
:freebsd,386
およびfreebsd,arm
ビルドタグを持つファイルで、32bitアーキテクチャ向けです。src/pkg/syscall/route_freebsd_64bit.go
:freebsd,amd64
ビルドタグを持つファイルで、64bitアーキテクチャ向けです。
-
FreeBSD 10対応の条件分岐:
src/pkg/syscall/route_freebsd_32bit.go
内のparseInterfaceMessage
関数では、freebsdVersion >= 1000000
という条件分岐が追加されています。- FreeBSD 10以降の場合:
ifMsghdr
構造体(FreeBSD 10で変更されたmbuf
ヘッダビューに対応する)にキャストし、Hwassist
,Epoch
,Lastchange
といったフィールドを新しい構造からInterfaceMessage.Header.Data
にコピーします。これにより、FreeBSD 10の新しいmbuf
構造に対応したデータパースが可能になります。 - FreeBSD 10より前の場合: 従来の
InterfaceMessage
構造体に直接キャストし、既存のパースロジックを維持します。
一方、
src/pkg/syscall/route_freebsd_64bit.go
では、parseInterfaceMessage
関数内にバージョンチェックの条件分岐がありません。これは、64bitアーキテクチャではifMsghdr
の変更がInterfaceMessage
のパースに影響を与えないか、あるいは別の方法で対応されているためと考えられます。コミットメッセージや変更内容からは、32bit環境でのみmbuf
の構造変更が直接的な問題を引き起こしていた可能性が示唆されます。 - FreeBSD 10以降の場合:
このアプローチにより、GoプログラムはFreeBSDの異なるバージョン間で互換性を保ちながら、ネットワークインターフェース情報を正確に処理できるようになります。
コアとなるコードの変更箇所
src/pkg/syscall/route_freebsd.go
--- a/src/pkg/syscall/route_freebsd.go
+++ b/src/pkg/syscall/route_freebsd.go
@@ -8,14 +8,20 @@ package syscall
import "unsafe"
+// See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html.
+var freebsdVersion uint32
+
+func init() {
+ freebsdVersion, _ = SysctlUint32("kern.osreldate")
+}
+
func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage {
switch any.Type {
case RTM_ADD, RTM_DELETE, RTM_CHANGE, RTM_GET, RTM_LOSING, RTM_REDIRECT, RTM_MISS, RTM_LOCK, RTM_RESOLVE:
p := (*RouteMessage)(unsafe.Pointer(any))
return &RouteMessage{Header: p.Header, Data: b[SizeofRtMsghdr:any.Msglen]}
case RTM_IFINFO:
-\t\tp := (*InterfaceMessage)(unsafe.Pointer(any))\n-\t\treturn &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]}\n+\t\treturn any.parseInterfaceMessage(b)
case RTM_IFANNOUNCE:
p := (*InterfaceAnnounceMessage)(unsafe.Pointer(any))
return &InterfaceAnnounceMessage{Header: p.Header}
src/pkg/syscall/route_freebsd_32bit.go
(新規ファイル)
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build freebsd,386 freebsd,arm
package syscall
import "unsafe"
func (any *anyMessage) parseInterfaceMessage(b []byte) *InterfaceMessage {
p := (*InterfaceMessage)(unsafe.Pointer(any))
// FreeBSD 10 and beyond have a restructured mbuf
// packet header view.
// See http://svnweb.freebsd.org/base?view=revision&revision=254804.
if freebsdVersion >= 1000000 {
m := (*ifMsghdr)(unsafe.Pointer(any))
p.Header.Data.Hwassist = uint32(m.Data.Hwassist)
p.Header.Data.Epoch = m.Data.Epoch
p.Header.Data.Lastchange = m.Data.Lastchange
return &InterfaceMessage{Header: p.Header, Data: b[sizeofIfMsghdr:any.Msglen]}
}
return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]}
}
src/pkg/syscall/route_freebsd_64bit.go
(新規ファイル)
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build freebsd,amd64
package syscall
import "unsafe"
func (any *anyMessage) parseInterfaceMessage(b []byte) *InterfaceMessage {
p := (*InterfaceMessage)(unsafe.Pointer(any))
return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]}
}
コアとなるコードの解説
src/pkg/syscall/route_freebsd.go
の変更
freebsdVersion
変数の追加とinit()
関数での初期化:var freebsdVersion uint32
でFreeBSDのバージョンを格納する変数を宣言し、init()
関数内でSysctlUint32("kern.osreldate")
を呼び出して、システムのkern.osreldate
の値をfreebsdVersion
に設定しています。これにより、プログラムの起動時にFreeBSDのバージョンが判別されます。RTM_IFINFO
処理の委譲:anyMessage.toRoutingMessage
関数内のRTM_IFINFO
ケースで、以前は直接InterfaceMessage
にキャストしていましたが、any.parseInterfaceMessage(b)
という新しいメソッドに処理を委譲するように変更されました。これにより、バージョンごとのパースロジックを外部に切り出すことが可能になりました。
src/pkg/syscall/route_freebsd_32bit.go
の新規追加と解説
このファイルは、32bitアーキテクチャ(freebsd,386
およびfreebsd,arm
)向けにコンパイルされます。
parseInterfaceMessage
関数: この関数は、anyMessage
(ルーティングメッセージの汎用ヘッダ)と生バイトスライスb
を受け取り、InterfaceMessage
構造体を返します。p := (*InterfaceMessage)(unsafe.Pointer(any))
: まず、汎用ヘッダany
をInterfaceMessage
型にunsafe.Pointerを使ってキャストします。これは、メッセージの共通部分を初期化するためです。if freebsdVersion >= 1000000
: ここがFreeBSD 10以降のバージョンに対応するための重要な条件分岐です。kern.osreldate
の値が1000000
以上であれば、FreeBSD 10以降と判断します。- FreeBSD 10以降の場合の処理:
m := (*ifMsghdr)(unsafe.Pointer(any))
: FreeBSD 10で変更されたmbuf
ヘッダビューに対応するifMsghdr
構造体にany
をキャストします。p.Header.Data.Hwassist = uint32(m.Data.Hwassist)
p.Header.Data.Epoch = m.Data.Epoch
p.Header.Data.Lastchange = m.Data.Lastchange
:ifMsghdr
から、Hwassist
、Epoch
、Lastchange
といったフィールドの値をInterfaceMessage
のHeader.Data
にコピーします。これは、FreeBSD 10でこれらのフィールドのオフセットやレイアウトが変更されたため、新しい構造から正しい値を取得し直す必要があるためです。return &InterfaceMessage{Header: p.Header, Data: b[sizeofIfMsghdr:any.Msglen]}
: 新しいifMsghdr
のサイズ(sizeofIfMsghdr
)を考慮して、データ部分を切り出してInterfaceMessage
を構築し返します。
- FreeBSD 10より前の場合の処理:
return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]}
: 従来のInterfaceMessage
のサイズ(SizeofIfMsghdr
)を考慮してデータ部分を切り出し、そのままInterfaceMessage
を構築して返します。
src/pkg/syscall/route_freebsd_64bit.go
の新規追加と解説
このファイルは、64bitアーキテクチャ(freebsd,amd64
)向けにコンパイルされます。
parseInterfaceMessage
関数: この関数は、32bit版と同様にanyMessage
と生バイトスライスb
を受け取り、InterfaceMessage
構造体を返します。p := (*InterfaceMessage)(unsafe.Pointer(any))
: 汎用ヘッダany
をInterfaceMessage
型にキャストします。return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]}
: 64bit環境では、FreeBSD 10のmbuf
構造変更がInterfaceMessage
のパースに直接的な影響を与えないためか、バージョンチェックの条件分岐なしで、従来のInterfaceMessage
のサイズを考慮してデータ部分を切り出し、InterfaceMessage
を構築して返しています。これは、32bitと64bitで構造体のアライメントやパディングが異なるため、影響範囲が異なることを示唆しています。
これらの変更により、Goのsyscall
パッケージはFreeBSDのバージョンに依存せず、InterfaceMessage
を正しく解釈できるようになり、FreeBSD 10以降のシステムでもネットワーク関連の低レベルな操作が適切に行えるようになりました。
関連リンク
- FreeBSD SVN Revision 254804: http://svnweb.freebsd.org/base?view=revision&revision=254804
- Go Issue #7193: https://golang.org/issue/7193
- Go CL 56980043: https://golang.org/cl/56980043
参考にした情報源リンク
- FreeBSD Handbook - FreeBSD Versions: http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html
- FreeBSD
kern.osreldate
documentation: https://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=3&manpath=FreeBSD+10.0-RELEASE mbuf
(memory buffer) in FreeBSD: https://www.freebsd.org/doc/en/books/arch-handbook/network-mbuf.html- Go
syscall
package documentation: https://pkg.go.dev/syscall - Web search results for "FreeBSD 10 mbuf packet header view change revision 254804"
- Web search results for "Go syscall.InterfaceMessage"
- Web search results for "kern.osreldate FreeBSD"