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

[インデックス 11537] ファイルの概要

このコミットは、Go言語の標準ライブラリにおいて、Windows環境でのIPv4マルチキャスト機能のサポートを強化するものです。具体的には、netパッケージとsyscallパッケージにIPv4マルチキャスト関連のヘルパー関数を追加し、それに伴いWindows上でのシンプルなIPv4マルチキャストテストを再度有効にしています。

コミット

commit 804f1882c582f05d55db812964ebe41a986b5eb1
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Wed Feb 1 14:14:04 2012 +0900

    net, syscall: add IPv4 multicast helpers for windows
    
    Also re-enable simple IPv4 multicast testing on windows.
    
    R=alex.brainman, rsc
    CC=golang-dev
    https://golang.org/cl/5605048

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/804f1882c582f05d55db812964ebe41a986b5eb1

元コミット内容

net, syscall: add IPv4 multicast helpers for windows

Also re-enable simple IPv4 multicast testing on windows.

R=alex.brainman, rsc
CC=golang-dev
https://golang.org/cl/5605048

変更の背景

Go言語のネットワークライブラリは、クロスプラットフォームでの動作を目指していますが、特定のOS固有の機能や挙動に対応するためには、OS固有の実装が必要となる場合があります。このコミット以前のGoのWindows環境では、IPv4マルチキャストに関する一部の機能が未実装(TODOコメントで示されていた)であり、関連するテストも無効化されていました。

この変更の背景には、Windows環境でのIPv4マルチキャスト通信をGoプログラムからより適切に制御できるようにするという目的があります。具体的には、マルチキャストパケットの送信インターフェースの指定、TTL(Time To Live)値の設定、ループバックの有効/無効化といった、マルチキャスト通信において重要なソケットオプションを設定するためのヘルパー関数が不足していました。これらの機能が実装されることで、Goで書かれたネットワークアプリケーションがWindows上でより高度なマルチキャスト通信を行えるようになります。また、テストの再有効化は、これらの新機能が正しく動作することを保証するために不可欠です。

前提知識の解説

IPv4マルチキャスト

IPv4マルチキャストは、単一の送信元から特定のグループに属する複数の受信者に対して、効率的にデータを送信するための通信方式です。ユニキャスト(1対1)やブロードキャスト(1対全員)とは異なり、マルチキャストでは「マルチキャストグループ」という概念が導入されます。データはマルチキャストグループアドレス宛に送信され、そのグループに参加しているすべてのホストがデータを受信できます。これにより、帯域幅の節約やネットワーク負荷の軽減が実現されます。

ソケットオプション

ソケットオプションは、ネットワークソケットの動作を制御するための設定項目です。setsockopt(設定)やgetsockopt(取得)といったシステムコールを通じて、アプリケーションはソケットの挙動を細かく調整できます。マルチキャスト通信においては、以下のようなソケットオプションが重要になります。

  • IP_MULTICAST_IF: マルチキャストパケットを送信する際に使用するローカルインターフェースを指定します。複数のネットワークインターフェースを持つホストで、どのインターフェースからマルチキャストパケットを送信するかを制御するために使用されます。
  • IP_MULTICAST_TTL: マルチキャストパケットのTTL(Time To Live)値を設定します。TTLはパケットが通過できるルーターのホップ数を制限し、ネットワーク上でのパケットの到達範囲を制御します。
  • IP_MULTICAST_LOOP: マルチキャストパケットのループバックを制御します。このオプションが有効な場合、送信元ホスト自身も送信したマルチキャストパケットを受信します。無効な場合、送信元ホストは自身の送信したパケットを受信しません。

Windows APIとシステムコール

Windowsオペレーティングシステムでは、アプリケーションがカーネルの機能にアクセスするためにWin32 APIが提供されています。ネットワーク関連の機能はWinsock(Windows Sockets)APIを通じて提供され、ソケットオプションの設定にはsetsockopt関数が使用されます。Go言語のsyscallパッケージは、これらのOS固有のシステムコールやAPIをGoプログラムから呼び出すためのインターフェースを提供します。

unsafe.PointerSizeof

Go言語のunsafeパッケージは、型安全性をバイパスして低レベルのメモリ操作を可能にするための機能を提供します。

  • unsafe.Pointer: 任意の型のポインタを保持できる汎用ポインタです。異なる型のポインタ間で変換を行う際に使用されます。
  • unsafe.Sizeof: 指定された式のメモリ上のサイズ(バイト単位)を返します。構造体や配列のサイズを動的に取得する際に使用されます。 これらの機能は、C言語の構造体をGoの構造体として扱い、システムコールに渡す際に、メモリレイアウトを正確に合わせるために必要となることがあります。

技術的詳細

このコミットは、Go言語のnetパッケージとsyscallパッケージにまたがる変更で、Windows環境でのIPv4マルチキャスト機能の不足を解消しています。

  1. net/multicast_test.goの変更:

    • TestSimpleListenMulticastUDP関数内で、これまでWindows環境ではテストをスキップしていたcase "windows": returnの行が削除されました。これにより、Windows上でもシンプルなIPv4マルチキャストのテストが実行されるようになります。これは、後述するヘルパー関数の実装によって、Windowsでのマルチキャスト機能が利用可能になったためです。
  2. net/sockoptip_windows.goの変更:

    • setIPv4MulticastInterface(fd *netFD, ifi *Interface) error関数が実装されました。この関数は、指定されたネットワークインターフェース(ifi)をマルチキャスト送信インターフェースとして設定します。
      • interfaceToIPv4Addr(ifi)でインターフェースのIPv4アドレスを取得します。
      • 取得したIPv4アドレスを4バイトの配列xにコピーします。
      • syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x)を呼び出して、ソケットオプションIP_MULTICAST_IFを設定します。SetsockoptInet4Addrは、後述するsyscallパッケージで新しく追加されたヘルパー関数です。
    • setIPv4MulticastTTL(fd *netFD, v int) error関数が実装されました。この関数は、マルチキャストパケットのTTL値を設定します。
      • syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, v)を呼び出して、ソケットオプションIP_MULTICAST_TTLを設定します。
    • setIPv4MulticastLoopback(fd *netFD, v bool) error関数が実装されました。この関数は、マルチキャストループバックの有効/無効を設定します。
      • syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))を呼び出して、ソケットオプションIP_MULTICAST_LOOPを設定します。boolint(v)bool値をintに変換するヘルパー関数です。
    • これらの関数は、以前は// TODO: Implement thisとコメントされ、syscall.EWINDOWS(Windowsでの未実装エラー)を返していました。今回の変更で、実際のソケットオプション設定ロジックが追加されました。
    • osパッケージがインポートされています。これはos.NewSyscallErrorを使用するためです。
  3. syscall/syscall_windows.goの変更:

    • SetsockoptInet4Addr(fd Handle, level, opt int, value [4]byte) (err error)関数が新しく追加されました。
      • この関数は、IPv4アドレス(4バイト配列)をソケットオプションとして設定するための汎用ヘルパーです。
      • 内部でSetsockopt関数を呼び出しており、value(4バイト配列)の先頭アドレスをunsafe.Pointer*byteにキャストし、サイズを4(バイト)として渡しています。これは、C言語のsetsockopt関数がポインタとサイズを引数として取ることに対応するためです。
  4. syscall/ztypes_windows.goの変更:

    • Windowsのソケットオプション定数として、以下の3つが追加されました。
      • IP_MULTICAST_IF = 0x9
      • IP_MULTICAST_TTL = 0xa
      • IP_MULTICAST_LOOP = 0xb
    • これらの定数は、Microsoftのサポート技術情報(KB257460)に記載されている値と一致しており、WindowsのWinsock APIにおけるIPv4マルチキャスト関連のソケットオプションに対応します。

これらの変更により、Go言語のnetパッケージはWindows環境においても、IPv4マルチキャスト通信のより詳細な制御が可能となり、クロスプラットフォームでのマルチキャストアプリケーション開発が容易になります。

コアとなるコードの変更箇所

src/pkg/net/multicast_test.go

--- a/src/pkg/net/multicast_test.go
+++ b/src/pkg/net/multicast_test.go
@@ -86,7 +86,7 @@ func TestListenMulticastUDP(t *testing.T) {
 
 func TestSimpleListenMulticastUDP(t *testing.T) {
 	switch runtime.GOOS {
-	case "plan9", "windows":
+	case "plan9":
 		return
 	}

src/pkg/net/sockoptip_windows.go

--- a/src/pkg/net/sockoptip_windows.go
+++ b/src/pkg/net/sockoptip_windows.go
@@ -7,6 +7,7 @@
 package net
 
 import (
+	"os"
 	"syscall"
 )
 
@@ -16,8 +17,19 @@ func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
 }
 
 func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
-	// TODO: Implement this
-	return syscall.EWINDOWS
+	ip, err := interfaceToIPv4Addr(ifi)
+	if err != nil {
+		return os.NewSyscallError("setsockopt", err)
+	}
+	var x [4]byte
+	copy(x[:], ip.To4())
+	fd.incref()
+	defer fd.decref()
+	err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x)
+	if err != nil {
+		return os.NewSyscallError("setsockopt", err)
+	}
+	return nil
 }
 
 func ipv4MulticastTTL(fd *netFD) (int, error) {
@@ -26,8 +38,14 @@ func ipv4MulticastTTL(fd *netFD) (int, error) {
 }
 
 func setIPv4MulticastTTL(fd *netFD, v int) error {
-	// TODO: Implement this
-	return syscall.EWINDOWS
+	fd.incref()
+	defer fd.decref()
+	err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, v)
+	if err != nil {
+		return os.NewSyscallError("setsockopt", err)
+	}
+	return nil
+
 }
 
 func ipv4MulticastLoopback(fd *netFD) (bool, error) {
@@ -36,8 +54,14 @@ func ipv4MulticastLoopback(fd *netFD) (bool, error) {
 }
 
 func setIPv4MulticastLoopback(fd *netFD, v bool) error {
-	// TODO: Implement this
-	return syscall.EWINDOWS
+	fd.incref()
+	defer fd.decref()
+	err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))
+	if err != nil {
+		return os.NewSyscallError("setsockopt", err)
+	}
+	return nil
+
 }
 
 func ipv4ReceiveInterface(fd *netFD) (bool, error) {

src/pkg/syscall/syscall_windows.go

--- a/src/pkg/syscall/syscall_windows.go
+++ b/src/pkg/syscall/syscall_windows.go
@@ -657,6 +657,9 @@ type IPv6Mreq struct {
 
 func GetsockoptInt(fd Handle, level, opt int) (int, error)              { return -1, EWINDOWS }
 func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (err error) { return EWINDOWS }
+func SetsockoptInet4Addr(fd Handle, level, opt int, value [4]byte) (err error) {
+	return Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(&value[0])), 4)
+}
 func SetsockoptIPMreq(fd Handle, level, opt int, mreq *IPMreq) (err error) {
 	return Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(mreq)), int32(unsafe.Sizeof(*mreq)))
 }

src/pkg/syscall/ztypes_windows.go

--- a/src/pkg/syscall/ztypes_windows.go
+++ b/src/pkg/syscall/ztypes_windows.go
@@ -389,8 +389,13 @@ const (
 	SO_SNDBUF                = 0x1001
 	SO_UPDATE_ACCEPT_CONTEXT = 0x700b
 
+	// cf. http://support.microsoft.com/default.aspx?scid=kb;en-us;257460
+
 	IP_TOS             = 0x3
 	IP_TTL             = 0x4
+	IP_MULTICAST_IF    = 0x9
+	IP_MULTICAST_TTL   = 0xa
+	IP_MULTICAST_LOOP  = 0xb
 	IP_ADD_MEMBERSHIP  = 0xc
 	IP_DROP_MEMBERSHIP = 0xd
 

コアとなるコードの解説

src/pkg/net/sockoptip_windows.go

このファイルは、Windows固有のIPソケットオプション設定に関するロジックをカプセル化しています。

  • setIPv4MulticastInterface:

    • interfaceToIPv4Addr(ifi): ネットワークインターフェースオブジェクトifiから、そのインターフェースに割り当てられたIPv4アドレスを取得します。
    • var x [4]byte; copy(x[:], ip.To4()): 取得したnet.IP型のアドレスを、syscall.SetsockoptInet4Addrが期待する4バイトのバイト配列xに変換してコピーします。
    • syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x): 実際にソケットオプションを設定する部分です。
      • fd.sysfd: ソケットのファイルディスクリプタ(Windowsではハンドル)。
      • syscall.IPPROTO_IP: IPプロトコルレベルのオプションであることを示します。
      • syscall.IP_MULTICAST_IF: マルチキャスト送信インターフェースを指定するためのソケットオプション定数。
      • x: 設定するIPv4アドレス(4バイト配列)。
    • fd.incref()fd.decref(): netFDの参照カウントを管理し、ソケットが適切にクローズされるようにします。
    • os.NewSyscallError("setsockopt", err): システムコールエラーが発生した場合に、より詳細なエラー情報を含むos.SyscallErrorを生成します。
  • setIPv4MulticastTTL:

    • syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, v): マルチキャストTTL値を設定します。SetsockoptIntは整数値を設定するためのヘルパー関数です。
  • setIPv4MulticastLoopback:

    • syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)): マルチキャストループバックの有効/無効を設定します。boolint(v)trueを1、falseを0に変換します。

src/pkg/syscall/syscall_windows.go

このファイルは、Windows固有のシステムコールをGoから呼び出すためのラッパー関数を提供します。

  • SetsockoptInet4Addr:
    • Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(&value[0])), 4): この関数が、実際のWindows APIのsetsockoptに相当する低レベルのSetsockopt関数を呼び出します。
      • (*byte)(unsafe.Pointer(&value[0])): 4バイト配列valueの先頭要素のアドレスをunsafe.Pointerを介して*byte型にキャストしています。これは、Setsockopt関数がオプション値のポインタを*byteとして受け取るためです。
      • 4: オプション値のサイズ(IPv4アドレスは4バイト)を指定します。

src/pkg/syscall/ztypes_windows.go

このファイルは、Windows APIで使用される定数や構造体のGo言語での定義を含んでいます。

  • IP_MULTICAST_IF, IP_MULTICAST_TTL, IP_MULTICAST_LOOP:
    • これらの定数は、WindowsのWinsock APIで定義されているソケットオプションの識別子です。Goのsyscallパッケージがこれらの定数を定義することで、GoプログラムからWindows固有のソケットオプションを直接参照できるようになります。コメントにあるMicrosoftのKB記事は、これらの定数の由来と意味を裏付けています。

これらの変更により、Goのnetパッケージは、Windows環境でのIPv4マルチキャスト通信において、よりきめ細やかな制御を可能にし、他のOSと同様の機能を提供できるようになりました。

関連リンク

参考にした情報源リンク