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

[インデックス 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言語の概念に関する知識が必要です。

  1. 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ビットがネットワーク部であることを示します。
  2. 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() メソッド: 特定のネットワークインターフェースに割り当てられたアドレスのリストを返します。
  3. Go言語の syscall パッケージ:

    • オペレーティングシステム(OS)の低レベルなシステムコールにアクセスするためのパッケージです。ネットワークインターフェース情報の取得など、OS固有の機能にアクセスする際に使用されます。
    • syscall.RouteRIB: ルーティング情報ベース(RIB)から情報を取得するためのシステムコールです。ネットワークインターフェース情報やルーティングテーブルの情報を取得するために使用されます。
    • syscall.NetlinkRIB: LinuxカーネルのNetlinkソケットを介してネットワーク情報を取得するためのシステムコールです。Linuxシステムでのインターフェース情報取得に利用されます。
    • syscall.Sockaddr: ソケットアドレスを表すインターフェースです。SockaddrInet4(IPv4)や SockaddrInet6(IPv6)などの具体的な型があります。
    • syscall.InterfaceAddrMessage: ネットワークインターフェースアドレスに関するルーティングメッセージの構造体です。
  4. OSごとのネットワーク情報取得メカニズム:

    • BSD系OS (macOS, FreeBSDなど): sysctl やルーティングソケット(RouteRIB)を介してネットワークインターフェース情報を取得します。
    • Linux: Netlink ソケット(NetlinkRIB)を介してネットワークインターフェース情報を取得します。

技術的詳細

このコミットの主要な技術的変更点は、net パッケージ内のインターフェースアドレス取得関数が IPAddr のスライスではなく IPNet のスライスを返すように修正されたことです。これにより、IPアドレスだけでなく、そのアドレスに対応するネットマスクも同時に提供されるようになります。

具体的な変更は以下のファイルに及びます。

  • src/pkg/net/interface_bsd.go: BSD系OS(macOS, FreeBSDなど)向けのインターフェース情報取得ロジックが含まれています。

    • interfaceAddrTable 関数が []Addr を返すように変更され、内部で newAddr 関数が AddrIPNet を実装するインターフェース)を返すように修正されました。
    • newAddr 関数は、syscall.ParseRoutingSockaddr から取得したソケットアドレス情報(IPアドレスとネットマスク)を基に IPNet 構造体を構築するようになりました。特に、RTAX_IFA(インターフェースアドレス)と RTAX_NETMASK(ネットマスク)のソケットアドレスを適切に解析し、IPNetIPMask フィールドに設定します。
    • エラーハンドリングの変数が 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" という文字列を返します。これにより、IPNetnet.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.SockaddrInet4syscall.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:

    • InterfaceAddrMessagesockaddr() メソッド内で、RTAX_NETMASK の処理が改善され、AF_UNSPEC の場合の AF_INET へのフォールバックが明示的に行われるようになりました。

コアとなるコードの解説

ここでは、特に重要な src/pkg/net/interface_bsd.gosrc/pkg/net/interface_linux.go における newAddr 関数の変更に焦点を当てて解説します。これらの関数は、OSから取得した生のアドレス情報をGoの net.IPNet 構造体に変換する役割を担っています。

src/pkg/net/interface_bsd.gonewAddr 関数

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アドレスでどちらがネットマスクかを判断しています。IPv4Maskmake(IPMask, IPv6len) を使用して、適切な IPMask を生成しています。

src/pkg/net/interface_linux.gonewAddr 関数

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 構造体として統一的に提供できるようになりました。

関連リンク

参考にした情報源リンク