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

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

このコミットは、Go言語の標準ライブラリnetパッケージにおいて、IPConnUDPConnにIP補助データ(ancillary data)を扱うためのReadMsgIPおよびWriteMsgIPメソッドを追加するものです。これにより、ソケット制御メッセージを介してIP補助データにアクセスできるようになります。

変更されたファイルは以下の通りです。

  • src/pkg/net/iprawsock_posix.go: 33行追加
  • src/pkg/net/udpsock_posix.go: 37行追加

合計で70行が追加されました。

コミット

commit 4b9e8415dee1587933a2d175966737c429f85f27
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Tue Sep 25 06:57:32 2012 +0900

    net: add read, write message methods to IPConn, UDPConn
    
    Both methods allow to access the IP ancillary data through
    socket control messages.
    
    This CL is required for CL 6482044; go.net/ipv4: new package.
    
    R=rsc, r, dave
    CC=golang-dev
    https://golang.org/cl/6426047

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

https://github.com/golang/go/commit/4b9e8415dee1587933a2d175966737c429f85f27

元コミット内容

net: add read, write message methods to IPConn, UDPConn

Both methods allow to access the IP ancillary data through
socket control messages.

This CL is required for CL 6482044; go.net/ipv4: new package.

R=rsc, r, dave
CC=golang-dev
https://golang.org/cl/6426047

変更の背景

このコミットの主な目的は、Go言語のnetパッケージにおけるIPConn(IPソケット接続)とUDPConn(UDPソケット接続)が、IP補助データ(ancillary data)をソケット制御メッセージ(socket control messages)を介して送受信できるようにすることです。

特に、この変更はCL 6482044、すなわちgo.net/ipv4パッケージの新規導入に必要とされています。go.net/ipv4パッケージは、IPv4のIPレベルソケットオプションをより詳細に制御するための機能を提供します。これには、Type-of-Service (TOS) や Time-to-Live (TTL) の設定、マルチキャストオプションの管理などが含まれます。これらの高度なネットワーク機能を実現するためには、通常のデータペイロードとは別に、IPヘッダ情報などの補助データを送受信するメカニズムが必要となります。

既存のReadFromWriteToメソッドでは、データペイロードのみを扱っており、IP補助データへのアクセスができませんでした。このコミットで追加されるReadMsgIPWriteMsgIPメソッドは、このギャップを埋め、go.net/ipv4のような低レベルネットワーク操作を可能にするための基盤を提供します。

前提知識の解説

IP補助データ (Ancillary Data) とソケット制御メッセージ (Socket Control Messages)

通常のネットワーク通信では、アプリケーションデータ(ペイロード)が送受信されます。しかし、IP層やトランスポート層のプロトコルには、ペイロードとは別に、パケットのルーティング、エラー処理、QoS(Quality of Service)などに関する追加情報が付随することがあります。これを「補助データ(Ancillary Data)」と呼びます。

補助データは、ソケットAPIにおいては「ソケット制御メッセージ(Socket Control Messages)」として扱われます。これは、sendmsgrecvmsgといったシステムコールを通じて、通常のデータとは別に送受信される特殊なメッセージです。例えば、IPヘッダのTTL(Time-to-Live)値、TOS(Type of Service)値、受信インターフェース情報、エラー情報などが補助データとしてやり取りされます。

IPConnUDPConn

  • IPConn: Go言語のnetパッケージにおけるIPソケット接続を表す型です。IP層での通信(例: ICMP、IGMP、または生のIPパケット)に使用されます。
  • UDPConn: Go言語のnetパッケージにおけるUDPソケット接続を表す型です。UDP(User Datagram Protocol)はコネクションレスなプロトコルで、信頼性よりも速度が重視される場合に用いられます。

これらのコネクションタイプは、ネットワーク通信の基本的な構成要素であり、このコミットではこれらの型に補助データ送受信の機能が拡張されます。

syscall.ReadMsgsyscall.WriteMsg

Go言語のsyscallパッケージは、オペレーティングシステムのシステムコールへの低レベルなインターフェースを提供します。

  • syscall.ReadMsg: recvmsgシステムコールをラップしたもので、ソケットからデータと補助データを読み取ります。通常のデータペイロードだけでなく、制御メッセージバッファ(oob)も引数として受け取り、補助データを格納します。
  • syscall.WriteMsg: sendmsgシステムコールをラップしたもので、ソケットにデータと補助データを書き込みます。通常のデータペイロードと制御メッセージバッファ(oob)を引数として受け取り、補助データを送信します。

これらのシステムコールは、IP補助データのような高度なソケット操作を行うために不可欠です。

go.net/ipv4パッケージ

go.net/ipv4は、Go言語のgolang.org/x/netサブリポジトリにあるパッケージで、IPv4のIPレベルソケットオプションをより詳細に制御するための機能を提供します。これには、以下のような機能が含まれます。

  • IPヘッダの操作: IPヘッダのフィールド(TOS, TTLなど)を読み書きする機能。
  • マルチキャストオプション: マルチキャストグループへの参加/脱退、マルチキャストTTLの設定など。
  • Raw IPソケットアクセス: 生のIPパケットを直接送受信する機能。

このパッケージは、標準のnetパッケージでは提供されていない低レベルなIP通信機能を提供し、ネットワークプログラミングのより高度なユースケースに対応します。今回のコミットは、このgo.net/ipv4パッケージがnetパッケージのIPConnUDPConnを通じて補助データにアクセスするための前提条件となります。

技術的詳細

このコミットでは、IPConnUDPConnのそれぞれにReadMsgIPWriteMsgIPという2つの新しいメソッドが追加されています。これらのメソッドは、内部的にnetFD(ファイルディスクリプタをラップする構造体)のReadMsgおよびWriteMsgメソッドを呼び出すことで、ソケット制御メッセージを介したIP補助データの送受信を実現しています。

ReadMsgIPメソッド

ReadMsgIPメソッドは、ソケットからパケットを読み取り、ペイロードをbに、関連する補助データ(out-of-band data)をoobにコピーします。

  • 引数:
    • b []byte: 受信したペイロードを格納するバイトスライス。
    • oob []byte: 受信した補助データを格納するバイトスライス。
  • 戻り値:
    • n int: bにコピーされたペイロードのバイト数。
    • oobn int: oobにコピーされた補助データのバイト数。
    • flags int: パケットに設定されていたフラグ(例: MSG_OOBMSG_TRUNCなど)。
    • addr *IPAddr または *UDPAddr: パケットの送信元アドレス。
    • err error: エラー情報。

内部では、c.fd.ReadMsg(b, oob)が呼び出されます。このReadMsgsyscall.Recvmsgシステムコールをラップしており、ペイロード、補助データ、フラグ、そして送信元ソケットアドレス(syscall.Sockaddr型)を返します。

返されたsyscall.Sockaddrは、その型(*syscall.SockaddrInet4または*syscall.SockaddrInet6)に応じて、適切な*IPAddrまたは*UDPAddrに変換されます。これにより、呼び出し元はGoのnetパッケージの型でアドレス情報を受け取ることができます。

WriteMsgIPメソッド

WriteMsgIPメソッドは、指定されたアドレスaddrへパケットを書き込み、ペイロードをbから、関連する補助データをoobからコピーします。

  • 引数:
    • b []byte: 送信するペイロードを含むバイトスライス。
    • oob []byte: 送信する補助データを含むバイトスライス。
    • addr *IPAddr または *UDPAddr: パケットの宛先アドレス。
  • 戻り値:
    • n int: 書き込まれたペイロードのバイト数。
    • oobn int: 書き込まれた補助データのバイト数。
    • err error: エラー情報。

内部では、まず宛先アドレスaddrc.fd.family(ソケットのファミリー、例: AF_INETAF_INET6)に対応するsyscall.Sockaddrに変換されます。その後、c.fd.WriteMsg(b, oob, sa)が呼び出されます。このWriteMsgsyscall.Sendmsgシステムコールをラップしており、ペイロードと補助データを指定されたソケットアドレスへ送信します。

UDPConnWriteMsgUDPでは、ソケットが既に接続済み(c.fd.isConnectedがtrue)の場合にErrWriteToConnectedエラーを返すチェックが追加されています。これは、接続済みUDPソケットではsendmsgに宛先アドレスを指定する必要がないため、誤用を防ぐためのものです。

これらのメソッドの追加により、Goのnetパッケージは、IP補助データを扱うための低レベルなソケット操作をサポートし、go.net/ipv4のようなより高度なネットワーク機能の実装を可能にしています。

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

src/pkg/net/iprawsock_posix.go

--- a/src/pkg/net/iprawsock_posix.go
+++ b/src/pkg/net/iprawsock_posix.go
@@ -98,6 +98,25 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
 	return n, uaddr.toAddr(), err
 }
 
+// ReadMsgIP reads a packet from c, copying the payload into b and the
+// associdated out-of-band data into oob.  It returns the number of
+// bytes copied into b, the number of bytes copied into oob, the flags
+// that were set on the packet and the source address of the packet.
+func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err error) {
+	if !c.ok() {
+		return 0, 0, 0, nil, syscall.EINVAL
+	}
+	var sa syscall.Sockaddr
+	n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob)
+	switch sa := sa.(type) {
+	case *syscall.SockaddrInet4:
+		addr = &IPAddr{sa.Addr[0:]}
+	case *syscall.SockaddrInet6:
+		addr = &IPAddr{sa.Addr[0:]}
+	}
+	return
+}
+
 // WriteToIP writes an IP packet to addr via c, copying the payload from b.
 //
 // WriteToIP can be made to time out and return
@@ -127,6 +146,20 @@ func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) {
 	return c.WriteToIP(b, a)
 }
 
+// WriteMsgIP writes a packet to addr via c, copying the payload from
+// b and the associated out-of-band data from oob.  It returns the
+// number of payload and out-of-band bytes written.
+func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error) {
+	if !c.ok() {
+		return 0, 0, syscall.EINVAL
+	}
+	sa, err := addr.sockaddr(c.fd.family)
+	if err != nil {
+		return 0, 0, &OpError{"write", c.fd.net, addr, err}
+	}
+	return c.fd.WriteMsg(b, oob, sa)
+}
+
 // DialIP connects to the remote address raddr on the network protocol netProto,
 // which must be "ip", "ip4", or "ip6" followed by a colon and a protocol number or name.
 func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {

src/pkg/net/udpsock_posix.go

--- a/src/pkg/net/udpsock_posix.go
+++ b/src/pkg/net/udpsock_posix.go
@@ -87,6 +87,26 @@ func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
 	return n, uaddr.toAddr(), err
 }
 
+// ReadMsgUDP reads a packet from c, copying the payload into b and
+// the associdated out-of-band data into oob.  It returns the number
+// of bytes copied into b, the number of bytes copied into oob, the
+// flags that were set on the packet and the source address of the
+// packet.
+func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) {
+	if !c.ok() {
+		return 0, 0, 0, nil, syscall.EINVAL
+	}
+	var sa syscall.Sockaddr
+	n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob)
+	switch sa := sa.(type) {
+	case *syscall.SockaddrInet4:
+		addr = &UDPAddr{sa.Addr[0:], sa.Port}
+	case *syscall.SockaddrInet6:
+		addr = &UDPAddr{sa.Addr[0:], sa.Port}
+	}
+	return
+}
+
 // WriteToUDP writes a UDP packet to addr via c, copying the payload from b.
 //
 // WriteToUDP can be made to time out and return
@@ -119,6 +139,23 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
 	return c.WriteToUDP(b, a)
 }
 
+// WriteMsgUDP writes a packet to addr via c, copying the payload from
+// b and the associated out-of-band data from oob.  It returns the
+// number of payload and out-of-band bytes written.
+func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) {
+	if !c.ok() {
+		return 0, 0, syscall.EINVAL
+	}
+	if c.fd.isConnected {
+		return 0, 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected}
+	}
+	sa, err := addr.sockaddr(c.fd.family)
+	if err != nil {
+		return 0, 0, &OpError{"write", c.fd.net, addr, err}
+	}
+	return c.fd.WriteMsg(b, oob, sa)
+}
+
 // DialUDP connects to the remote address raddr on the network net,\n // which must be "udp", "udp4", or "udp6".  If laddr is not nil, it is used
 // as the local address for the connection.

コアとなるコードの解説

このコミットで追加された主要な機能は、IPConnUDPConnにそれぞれReadMsgIP/ReadMsgUDPWriteMsgIP/WriteMsgUDPメソッドが追加されたことです。これらのメソッドは、IP補助データ(ancillary data)の送受信を可能にします。

IPConn.ReadMsgIP および UDPConn.ReadMsgUDP

これらのメソッドは、ソケットからデータと補助データを読み取るためのものです。

  1. if !c.ok() { return ... syscall.EINVAL }: まず、コネクションが有効な状態であるかを確認します。無効な場合はsyscall.EINVALエラーを返します。
  2. var sa syscall.Sockaddr: 送信元アドレスを格納するためのsyscall.Sockaddr型の変数を宣言します。
  3. n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob): 内部のnetFD(ファイルディスクリプタ)のReadMsgメソッドを呼び出します。このメソッドは、システムコールのrecvmsgをラップしており、以下の情報を返します。
    • n: 受信したペイロードのバイト数。
    • oobn: 受信した補助データのバイト数。
    • flags: 受信フラグ(例: MSG_OOBMSG_TRUNCなど)。
    • sa: 送信元ソケットアドレス。
    • err: エラー情報。
  4. switch sa := sa.(type): 受信したsyscall.Sockaddrの具体的な型をチェックします。
    • case *syscall.SockaddrInet4: IPv4アドレスの場合、sa.Addr[0:]からIPAddrまたはUDPAddrを構築します。
    • case *syscall.SockaddrInet6: IPv6アドレスの場合、sa.Addr[0:]からIPAddrまたはUDPAddrを構築します。UDPAddrの場合はポート番号も含まれます。
  5. return: 読み取ったデータ、補助データ、フラグ、送信元アドレス、エラーを返します。

IPConn.WriteMsgIP および UDPConn.WriteMsgUDP

これらのメソッドは、ソケットにデータと補助データを書き込むためのものです。

  1. if !c.ok() { return ... syscall.EINVAL }: コネクションが有効な状態であるかを確認します。
  2. if c.fd.isConnected { return ... ErrWriteToConnected } (UDPConnのみ): UDPConnの場合、ソケットが既に接続済みであるかを確認します。接続済みUDPソケットに対してsendmsgで宛先アドレスを指定することは通常ないため、このチェックは誤用を防ぐためのものです。
  3. sa, err := addr.sockaddr(c.fd.family): 引数で渡された宛先アドレス(*IPAddrまたは*UDPAddr)を、ソケットのファミリー(IPv4またはIPv6)に対応するsyscall.Sockaddr型に変換します。
  4. if err != nil { return ... &OpError{"write", ...} }: アドレス変換中にエラーが発生した場合、OpErrorを返します。
  5. return c.fd.WriteMsg(b, oob, sa): 内部のnetFDWriteMsgメソッドを呼び出します。このメソッドは、システムコールのsendmsgをラップしており、ペイロードbと補助データoobを指定されたソケットアドレスsaへ送信します。書き込まれたペイロードと補助データのバイト数を返します。

これらの変更により、Goのnetパッケージは、IP補助データという低レベルなネットワーク情報を扱う能力を獲得し、go.net/ipv4のようなより高度なネットワークプログラミングを可能にするための重要な基盤が整備されました。

関連リンク

参考にした情報源リンク