[インデックス 10937] ファイルの概要
このコミット(インデックス 10937、ハッシュ 836105679e1da85208e3a7a4e2f0a1f375d0a257
)は、Go言語の標準ライブラリである net
および syscall
パッケージにおけるネットワークインターフェースのアドレス表現方法を改善するものです。具体的には、インターフェースのアドレスとネットマスクをより正確に表現するために、InterfaceAddrs
関数および Interface
型の Addrs
メソッドが IPAddr
構造体のスライスではなく IPNet
構造体のスライスを返すように変更されました。これにより、IPアドレスだけでなく、そのアドレスが属するネットワークの範囲(ネットマスク)も同時に取得できるようになり、ネットワーク設定のより完全な情報を提供します。
コミット
commit 836105679e1da85208e3a7a4e2f0a1f375d0a257
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Wed Dec 21 21:39:00 2011 +0900
net, syscall: interface address and mask
This CL makes both InterfaceAddrs and Addrs method on Interface
return IPNet struct for representing interface address and mask
like below:
interface "lo0": flags "up|loopback|multicast", ifindex 1, mtu 16384
interface address "fe80::1/64"
interface address "127.0.0.1/8"
interface address "::1/128"
joined group address "ff02::fb"
joined group address "224.0.0.251"
joined group address "ff02::2:65d0:d71e"
joined group address "224.0.0.1"
joined group address "ff01::1"
joined group address "ff02::1"
joined group address "ff02::1:ff00:1"
Fixes #2571.
R=rsc
CC=golang-dev
https://golang.org/cl/5489062
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/836105679e1da85208e3a7a4e2f0a1f375d0a257
元コミット内容
net, syscall: interface address and mask
This CL makes both InterfaceAddrs and Addrs method on Interface
return IPNet struct for representing interface address and mask
like below:
interface "lo0": flags "up|loopback|multicast", ifindex 1, mtu 16384
interface address "fe80::1/64"
interface address "127.0.0.1/8"
interface address "::1/128"
joined group address "ff02::fb"
joined group address "224.0.0.251"
joined group address "ff02::2:65d0:d71e"
joined group address "224.0.0.1"
joined group address "ff01::1"
joined group address "ff02::1"
joined group address "ff02::1:ff00:1"
Fixes #2571.
R=rsc
CC=golang-dev
https://golang.org/cl/5489062
変更の背景
Go言語の net
パッケージは、ネットワークインターフェースに関する情報を提供する機能を持っています。以前は、インターフェースに割り当てられたIPアドレスを取得する際に、アドレス自体は取得できましたが、そのアドレスが属するサブネットの範囲を示すネットマスクの情報が欠落していました。これは、特にネットワーク設定を詳細に分析したり、特定のサブネット内のホストを識別したりするアプリケーションにとって不十分な情報でした。
このコミットは、Go issue #2571("net: InterfaceAddrs should return IPNet")を修正するために作成されました。このIssueでは、InterfaceAddrs
関数がIPアドレスだけでなく、関連するネットマスクも提供すべきであると指摘されていました。ネットマスクは、IPアドレスがどのネットワークセグメントに属するかを定義するために不可欠な情報であり、これがないと、例えばCIDR表記(例: 192.168.1.10/24
)のような完全なネットワークアドレス表現ができませんでした。
この変更により、net
パッケージが提供するネットワークインターフェース情報がより完全になり、ユーザーはIPアドレスとネットマスクを組み合わせて、インターフェースのネットワーク構成を正確に把握できるようになります。
前提知識の解説
このコミットを理解するためには、以下のネットワークおよびGo言語の概念に関する知識が必要です。
-
IPアドレスとネットマスク:
- IPアドレス: ネットワーク上のデバイスを一意に識別するための数値ラベルです。IPv4(例:
192.168.1.10
)とIPv6(例:fe80::1
)があります。 - ネットマスク (Subnet Mask): IPアドレスのどの部分がネットワークアドレスで、どの部分がホストアドレスであるかを区別するために使用される32ビット(IPv4の場合)または128ビット(IPv6の場合)の数値です。これにより、IPアドレスが属するサブネットの範囲が定義されます。
- CIDR (Classless Inter-Domain Routing) 表記: IPアドレスとネットマスクを簡潔に表現する方法です。IPアドレスの後にスラッシュとプレフィックス長(ネットワークアドレス部のビット数)を続けます。例:
192.168.1.0/24
は、192.168.1.0
がネットワークアドレスで、最初の24ビットがネットワーク部であることを示します。
- IPアドレス: ネットワーク上のデバイスを一意に識別するための数値ラベルです。IPv4(例:
-
Go言語の
net
パッケージ:- Go言語の標準ライブラリで、ネットワークI/O機能を提供します。TCP/IP、UDP、DNSルックアップ、ネットワークインターフェース情報の取得などが含まれます。
net.IP
: IPアドレスを表すバイトスライスです。net.IPMask
: ネットマスクを表すバイトスライスです。net.IPNet
構造体: IPアドレスとネットマスクを組み合わせて表現する構造体です。type IPNet struct { IP IP // network number Mask IPMask // network mask }
net.Interface
構造体: ネットワークインターフェースの情報を表します(名前、インデックス、MTU、フラグ、ハードウェアアドレスなど)。net.InterfaceAddrs()
関数: システム上のすべてのネットワークインターフェースに割り当てられたアドレスのリストを返します。(*net.Interface).Addrs()
メソッド: 特定のネットワークインターフェースに割り当てられたアドレスのリストを返します。
-
Go言語の
syscall
パッケージ:- オペレーティングシステム(OS)の低レベルなシステムコールにアクセスするためのパッケージです。ネットワークインターフェース情報の取得など、OS固有の機能にアクセスする際に使用されます。
syscall.RouteRIB
: ルーティング情報ベース(RIB)から情報を取得するためのシステムコールです。ネットワークインターフェース情報やルーティングテーブルの情報を取得するために使用されます。syscall.NetlinkRIB
: LinuxカーネルのNetlinkソケットを介してネットワーク情報を取得するためのシステムコールです。Linuxシステムでのインターフェース情報取得に利用されます。syscall.Sockaddr
: ソケットアドレスを表すインターフェースです。SockaddrInet4
(IPv4)やSockaddrInet6
(IPv6)などの具体的な型があります。syscall.InterfaceAddrMessage
: ネットワークインターフェースアドレスに関するルーティングメッセージの構造体です。
-
OSごとのネットワーク情報取得メカニズム:
- BSD系OS (macOS, FreeBSDなど):
sysctl
やルーティングソケット(RouteRIB
)を介してネットワークインターフェース情報を取得します。 - Linux:
Netlink
ソケット(NetlinkRIB
)を介してネットワークインターフェース情報を取得します。
- BSD系OS (macOS, FreeBSDなど):
技術的詳細
このコミットの主要な技術的変更点は、net
パッケージ内のインターフェースアドレス取得関数が IPAddr
のスライスではなく IPNet
のスライスを返すように修正されたことです。これにより、IPアドレスだけでなく、そのアドレスに対応するネットマスクも同時に提供されるようになります。
具体的な変更は以下のファイルに及びます。
-
src/pkg/net/interface_bsd.go
: BSD系OS(macOS, FreeBSDなど)向けのインターフェース情報取得ロジックが含まれています。interfaceAddrTable
関数が[]Addr
を返すように変更され、内部でnewAddr
関数がAddr
(IPNet
を実装するインターフェース)を返すように修正されました。newAddr
関数は、syscall.ParseRoutingSockaddr
から取得したソケットアドレス情報(IPアドレスとネットマスク)を基にIPNet
構造体を構築するようになりました。特に、RTAX_IFA
(インターフェースアドレス)とRTAX_NETMASK
(ネットマスク)のソケットアドレスを適切に解析し、IPNet
のIP
とMask
フィールドに設定します。- エラーハンドリングの変数が
e
からerr
に統一されました。
-
src/pkg/net/interface_linux.go
: Linux向けのインターフェース情報取得ロジックが含まれています。interfaceAddrTable
関数が[]Addr
を返すように変更され、内部でnewAddr
関数がAddr
を返すように修正されました。newAddr
関数は、Netlinkメッセージから取得した属性(syscall.NetlinkRouteAttr
)とアドレスファミリー、プレフィックス長(pfxlen
)を基にIPNet
構造体を構築するようになりました。IPv4とIPv6の両方に対応し、CIDRMask
を使用してプレフィックス長からIPMask
を生成します。newLink
関数の引数順序が変更されました。- エラーハンドリングの変数が
e
からerr
に統一されました。
-
src/pkg/net/interface_test.go
: ネットワークインターフェース関連のテストコードです。TestInterfaces
およびTestInterfaceAddrs
関数が、返されるアドレスがIPNet
型であることを検証するように更新されました。testAddrs
およびtestMulticastAddrs
というヘルパー関数が導入され、返されたアドレスの型が*IPAddr
または*IPNet
であることを確認するようになりました。これにより、テストの可読性と再利用性が向上しています。
-
src/pkg/net/ip.go
: IPアドレスとネットワーク関連のユーティリティ関数が含まれています。IPNet
構造体にNetwork() string
メソッドが追加されました。これはnet.Addr
インターフェースを満たすために必要で、"ip+net"
という文字列を返します。これにより、IPNet
がnet.Addr
として扱えるようになり、インターフェースアドレスの統一的な表現が可能になります。
-
src/pkg/syscall/route_bsd.go
: BSD系OSのルーティングソケット関連のシステムコールラッパーが含まれています。ParseRoutingSockaddr
関数内で、RTAX_NETMASK
の処理が改善されました。特に、AF_UNSPEC
ファミリーが指定された場合にAF_INET
と見なす古い慣習に対応し、ネットマスクが正しく解析されるように修正されました。- エラーハンドリングの変数が
e
からerr
に統一されました。
これらの変更により、Goの net
パッケージは、ネットワークインターフェースのアドレス情報をより正確かつ包括的に提供できるようになり、ユーザーはIPアドレスとネットマスクをセットで取得し、より高度なネットワークプログラミングが可能になります。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルと、それぞれの変更の概要は以下の通りです。
-
src/pkg/net/interface_bsd.go
:interfaceAddrTable
関数内で、newAddr
の呼び出しがappend(ifat, ifa...)
からappend(ifat, ifa)
に変更され、newAddr
が単一のAddr
を返すように修正されました。newAddr
関数のシグネチャが([]Addr, error)
から(Addr, error)
に変更され、IPNet
構造体を返すように実装が変更されました。syscall.SockaddrInet4
とsyscall.SockaddrInet6
の両方で、IPアドレスとネットマスクをIPNet
に設定するロジックが追加されました。
-
src/pkg/net/interface_linux.go
:interfaceAddrTable
関数内で、newAddr
の呼び出しがappend(ifat, newAddr(...)...)
からappend(ifat, newAddr(...))
に変更され、newAddr
が単一のAddr
を返すように修正されました。newAddr
関数のシグネチャが([]Addr)
から(Addr)
に変更され、IPNet
構造体を返すように実装が変更されました。syscall.IFA_ADDRESS
属性からIPアドレスを取得し、pfxlen
(プレフィックス長)を使用してCIDRMask
でネットマスクを生成するロジックが追加されました。
-
src/pkg/net/interface_test.go
:TestInterfaces
およびTestInterfaceAddrs
内で、インターフェースアドレスのログ出力と検証ロジックがtestInterfaceAddrs
およびtestAddrs
ヘルパー関数に抽出されました。testAddrs
関数が、返されたAddr
の型が*IPAddr
または*IPNet
であることを確認するswitch
ステートメントを含むようになりました。
-
src/pkg/net/ip.go
:IPNet
構造体にNetwork() string
メソッドが追加されました。これはnet.Addr
インターフェースを満たすために必要です。
-
src/pkg/syscall/route_bsd.go
:InterfaceAddrMessage
のsockaddr()
メソッド内で、RTAX_NETMASK
の処理が改善され、AF_UNSPEC
の場合のAF_INET
へのフォールバックが明示的に行われるようになりました。
コアとなるコードの解説
ここでは、特に重要な src/pkg/net/interface_bsd.go
と src/pkg/net/interface_linux.go
における newAddr
関数の変更に焦点を当てて解説します。これらの関数は、OSから取得した生のアドレス情報をGoの net.IPNet
構造体に変換する役割を担っています。
src/pkg/net/interface_bsd.go
の newAddr
関数
func newAddr(m *syscall.InterfaceAddrMessage) (Addr, error) {
ifa := &IPNet{} // IPNet構造体のポインタを初期化
sas, err := syscall.ParseRoutingSockaddr(m) // ルーティングソケットアドレスを解析
if err != nil {
return nil, os.NewSyscallError("route sockaddr", err)
}
for i, s := range sas { // 解析されたソケットアドレスをループ
switch v := s.(type) {
case *syscall.SockaddrInet4: // IPv4アドレスの場合
switch i {
case 0: // 最初のソケットアドレスがネットマスクの場合
ifa.Mask = IPv4Mask(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])
case 1: // 2番目のソケットアドレスがIPアドレスの場合
ifa.IP = IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])
}
case *syscall.SockaddrInet6: // IPv6アドレスの場合
switch i {
case 0: // 最初のソケットアドレスがネットマスクの場合
ifa.Mask = make(IPMask, IPv6len)
copy(ifa.Mask, v.Addr[:])
case 1: // 2番目のソケットアドレスがIPアドレスの場合
ifa.IP = make(IP, IPv6len)
copy(ifa.IP, v.Addr[:])
// KAMEベースのIPv6スタックの特殊処理
if ifa.IP.IsLinkLocalUnicast() {
ifa.IP[2], ifa.IP[3] = 0, 0
}
}
}
}
return ifa, nil // 構築されたIPNetを返す
}
この newAddr
関数は、syscall.InterfaceAddrMessage
からIPアドレスとネットマスクを抽出し、それらを net.IPNet
構造体にマッピングします。BSD系OSでは、ルーティングソケットメッセージ内にIPアドレスとネットマスクが別々の Sockaddr
として含まれることがあり、i
(インデックス)によってどちらがIPアドレスでどちらがネットマスクかを判断しています。IPv4Mask
や make(IPMask, IPv6len)
を使用して、適切な IPMask
を生成しています。
src/pkg/net/interface_linux.go
の newAddr
関数
func newAddr(attrs []syscall.NetlinkRouteAttr, family, pfxlen int) Addr {
ifa := &IPNet{} // IPNet構造体のポインタを初期化
for _, a := range attrs { // Netlink属性をループ
switch a.Attr.Type {
case syscall.IFA_ADDRESS: // アドレス属性の場合
switch family {
case syscall.AF_INET: // IPv4の場合
ifa.IP = IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3])
ifa.Mask = CIDRMask(pfxlen, 8*IPv4len) // プレフィックス長からネットマスクを生成
case syscall.AF_INET6: // IPv6の場合
ifa.IP = make(IP, IPv6len)
copy(ifa.IP, a.Value[:])
ifa.Mask = CIDRMask(pfxlen, 8*IPv6len) // プレフィックス長からネットマスクを生成
}
}
}
return ifa // 構築されたIPNetを返す
}
Linuxの newAddr
関数は、Netlink属性(syscall.NetlinkRouteAttr
)と、syscall.IfAddrmsg
から取得したアドレスファミリー(family
)およびプレフィックス長(pfxlen
)を使用します。Linuxでは、IPアドレスとプレフィックス長が直接提供されるため、CIDRMask
関数を使ってプレフィックス長から IPMask
を生成し、IPNet
構造体を構築します。これはBSD系OSとは異なるアプローチですが、最終的に IPNet
を返すという目的は同じです。
これらの変更により、Goの net
パッケージは、OSに依存することなく、ネットワークインターフェースのIPアドレスとネットマスクを IPNet
構造体として統一的に提供できるようになりました。
関連リンク
- Go issue #2571: net: InterfaceAddrs should return IPNet
- Go CL 5489062: https://golang.org/cl/5489062
参考にした情報源リンク
- Go Documentation:
net
package (https://pkg.go.dev/net) - Go Documentation:
syscall
package (https://pkg.go.dev/syscall) - CIDR (Classless Inter-Domain Routing) - Wikipedia (https://ja.wikipedia.org/wiki/CIDR)
- Netlink - Wikipedia (https://en.wikipedia.org/wiki/Netlink)
- Routing socket - Wikipedia (https://en.wikipedia.org/wiki/Routing_socket)