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

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

このコミットは、Go言語の実験的なSSHライブラリ(exp/ssh)において、SSHチャネルの初期ウィンドウサイズ広告が失われる問題を修正するものです。具体的には、channelOpenConfirmMsgで通知されるピアのウィンドウサイズを適切に処理し、チャネルのウィンドウ管理に反映させることで、一部のリモートサーバーとの互換性を向上させています。

コミット

  • コミットハッシュ: 90ec203318a167505ae1038bdcdc7d2ce664a1eb
  • Author: Dave Cheney dave@cheney.net
  • Date: Sun Nov 13 12:13:46 2011 -0500

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

https://github.com/golang/go/commit/90ec203318a167505ae1038bdcdc7d2ce664a1eb

元コミット内容

exp/ssh: ensure initial window advertisement is not lost

Some remote servers send a 0 window size in the channel
open confirm msg, others send a non zero window size. Make
sure this initial advertisement is not lost.

R=agl, rsc, cw
CC=golang-dev
https://golang.org/cl/5372083

変更の背景

SSHプロトコルでは、データフロー制御のために「ウィンドウサイズ」という概念を使用します。これは、受信側が一度に受け入れ可能なデータの量を送信側に通知するメカニズムです。これにより、受信バッファのオーバーフローを防ぎ、効率的なデータ転送を実現します。

このコミットが行われた当時、Go言語の実験的なSSHクライアント実装において、チャネルを開く際の初期ウィンドウサイズ広告の処理に問題がありました。具体的には、channelOpenConfirmMsg(チャネル開設確認メッセージ)でピア(リモートサーバー)から通知される初期ウィンドウサイズが、クライアント側で適切に処理されず、失われてしまう可能性がありました。

コミットメッセージによると、一部のリモートサーバーはchannelOpenConfirmMsgでゼロ以外のウィンドウサイズを通知する一方で、別のサーバーはゼロを通知することがありました。この差異に対応し、どちらの場合でもピアから通知された初期ウィンドウサイズを確実にクライアント側のウィンドウ管理に反映させることが、この変更の目的です。初期ウィンドウサイズが正しく設定されないと、データ転送が滞ったり、パフォーマンスが低下したりする可能性があります。

前提知識の解説

SSH (Secure Shell)

SSHは、ネットワークを介してコンピュータを安全に操作するためのプロトコルです。主にリモートログインやファイル転送(SCP, SFTP)に利用されます。暗号化によって通信内容を保護し、認証によって正当なユーザーのみがアクセスできるようにします。

SSHチャネル (SSH Channel)

SSHセッション内では、複数の論理的な「チャネル」を開くことができます。各チャネルは独立したデータストリームであり、例えば、シェルセッション、ポートフォワーディング、X11転送など、異なる目的のために使用されます。チャネルは、SSHプロトコルにおける多重化のメカニズムを提供します。

SSHウィンドウサイズ (SSH Window Size) とフロー制御

SSHプロトコルは、TCPのような信頼性のあるストリームプロトコル上で動作しますが、独自のフロー制御メカニズムを持っています。これが「ウィンドウサイズ」です。

  • ウィンドウサイズ: 各チャネルにおいて、受信側が送信側に対して「これだけのバイト数なら追加で受け取れる」と通知する値です。送信側はこのウィンドウサイズを超えてデータを送信してはなりません。
  • フロー制御の目的: 受信側のバッファが溢れるのを防ぎ、ネットワークの輻輳を緩和し、効率的なデータ転送を保証します。
  • ウィンドウの更新: 受信側はデータを受信するたびに、残りのウィンドウサイズを計算し、必要に応じてSSH_MSG_CHANNEL_WINDOW_ADJUSTメッセージを送信して、送信側にウィンドウサイズを「補充」するよう通知します。

channelOpenConfirmMsg

これは、SSHプロトコルにおいて、クライアントがチャネル開設要求(SSH_MSG_CHANNEL_OPEN)を送信した後、サーバーがその要求を承認した際に返送するメッセージです。このメッセージには、サーバー側のチャネルID、初期ウィンドウサイズ、最大パケットサイズなどの情報が含まれます。

  • MyId: サーバー側で割り当てられたチャネルID。
  • MyWindow: サーバーがこのチャネルで最初に受け入れ可能なバイト数(初期ウィンドウサイズ)。
  • MyMaxPacketSize: サーバーがこのチャネルで受け入れ可能な最大パケットサイズ。

このMyWindowの値が、クライアントがサーバーにデータを送信する際の初期の許容量となります。

技術的詳細

このコミットの核心は、SSHチャネルの確立フェーズにおけるフロー制御の初期化です。

SSHチャネルが開かれる際、クライアントとサーバーはそれぞれ自身のチャネルID、初期ウィンドウサイズ、最大パケットサイズを相手に通知します。クライアントがSSH_MSG_CHANNEL_OPENを送信し、サーバーがSSH_MSG_CHANNEL_OPEN_CONFIRMATION(GoのコードではchannelOpenConfirmMsgとして表現されている)を返します。このchannelOpenConfirmMsgには、サーバーがそのチャネルで受け入れ可能な初期ウィンドウサイズ(MyWindow)が含まれています。

問題は、Goのexp/sshクライアント実装が、このchannelOpenConfirmMsgで通知されるMyWindowの値を適切に利用していなかった点にありました。一部のサーバーはMyWindowにゼロ以外の値を設定していましたが、別のサーバーはゼロを設定していました。クライアントがこの初期ウィンドウサイズを無視したり、適切に処理しなかったりすると、クライアントがサーバーにデータを送信する際に、サーバー側のバッファが準備できていないにもかかわらずデータを送りつけてしまうか、あるいは、サーバーが既にデータを受け入れる準備ができているにもかかわらず、クライアントがウィンドウサイズがゼロであると誤解してデータ送信を停止してしまう、といった問題が発生する可能性がありました。

このコミットは、channelOpenConfirmMsgを受信した際に、そのメッセージに含まれるMyWindowの値を、クライアント側のチャネルのウィンドウ管理機構(ch.winチャネル)に即座に送信することで、この初期ウィンドウサイズ広告が失われないように修正しています。これにより、クライアントはサーバーが最初に受け入れ可能なデータ量を正確に把握し、それに基づいてデータ送信を開始できるようになります。

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

変更はsrc/pkg/exp/ssh/client.goファイルの一箇所のみです。

--- a/src/pkg/exp/ssh/client.go
+++ b/src/pkg/exp/ssh/client.go
@@ -195,6 +195,7 @@ func (c *ClientConn) openChan(typ string) (*clientChan, error) {
 	switch msg := (<-ch.msg).(type) {
 	case *channelOpenConfirmMsg:
 		ch.peersId = msg.MyId
+		ch.win <- int(msg.MyWindow)
 	case *channelOpenFailureMsg:
 		c.chanlist.remove(ch.id)
 		return nil, errors.New(msg.Message)

コアとなるコードの解説

func (c *ClientConn) openChan(typ string) (*clientChan, error)関数は、新しいSSHチャネルを開く処理を担当しています。

この関数内で、switch msg := (<-ch.msg).(type)ブロックは、チャネル開設要求に対するサーバーからの応答メッセージを処理しています。

  • ch.msgは、サーバーから受信したチャネル関連のメッセージが送られてくるチャネルです。
  • case *channelOpenConfirmMsg:は、サーバーがチャネル開設を承認したことを示すchannelOpenConfirmMsgを受信した場合の処理です。

変更前のコードでは、channelOpenConfirmMsgを受信した際に、ピアのチャネルID (msg.MyId) はch.peersIdに設定されていましたが、ピアの初期ウィンドウサイズ (msg.MyWindow) は特に処理されていませんでした。

追加された行:

ch.win <- int(msg.MyWindow)

この行が、このコミットの主要な変更点です。

  • msg.MyWindow: channelOpenConfirmMsgに含まれる、サーバーがこのチャネルで最初に受け入れ可能なバイト数(初期ウィンドウサイズ)です。これはuint32型です。
  • int(msg.MyWindow): uint32型のMyWindowint型にキャストしています。
  • ch.win <- ...: ch.winは、このチャネルのウィンドウサイズを管理するためのGoチャネル(おそらくchan int型)です。このチャネルにmsg.MyWindowの値を送信することで、クライアント側のチャネル実装が、サーバーから通知された初期ウィンドウサイズを認識し、それに基づいてデータ送信のフロー制御を開始できるようになります。

これにより、サーバーがゼロ以外の初期ウィンドウサイズを通知した場合でも、その情報が適切にクライアントのウィンドウ管理に反映され、データ転送がスムーズに開始されるようになります。また、ゼロを通知した場合でも、そのゼロが正しく認識され、ウィンドウ調整メッセージが来るまでデータ送信が抑制されることになります。

関連リンク

参考にした情報源リンク

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

このコミットは、Go言語の実験的なSSHライブラリ(exp/ssh)において、SSHチャネルの初期ウィンドウサイズ広告が失われる問題を修正するものです。具体的には、channelOpenConfirmMsgで通知されるピアのウィンドウサイズを適切に処理し、チャネルのウィンドウ管理に反映させることで、一部のリモートサーバーとの互換性を向上させています。

コミット

  • コミットハッシュ: 90ec203318a167505ae1038bdcdc7d2ce664a1eb
  • Author: Dave Cheney dave@cheney.net
  • Date: Sun Nov 13 12:13:46 2011 -0500

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

https://github.com/golang/go/commit/90ec203318a167505ae1038bdcdc7d2ce664a1eb

元コミット内容

exp/ssh: ensure initial window advertisement is not lost

Some remote servers send a 0 window size in the channel
open confirm msg, others send a non zero window size. Make
sure this initial advertisement is not lost.

R=agl, rsc, cw
CC=golang-dev
https://golang.org/cl/5372083

変更の背景

SSHプロトコルでは、データフロー制御のために「ウィンドウサイズ」という概念を使用します。これは、受信側が一度に受け入れ可能なデータの量を送信側に通知するメカニズムです。これにより、受信バッファのオーバーフローを防ぎ、効率的なデータ転送を実現します。

このコミットが行われた当時、Go言語の実験的なSSHクライアント実装において、チャネルを開く際の初期ウィンドウサイズ広告の処理に問題がありました。具体的には、channelOpenConfirmMsg(チャネル開設確認メッセージ)でピア(リモートサーバー)から通知される初期ウィンドウサイズが、クライアント側で適切に処理されず、失われてしまう可能性がありました。

コミットメッセージによると、一部のリモートサーバーはchannelOpenConfirmMsgでゼロ以外のウィンドウサイズを通知する一方で、別のサーバーはゼロを通知することがありました。この差異に対応し、どちらの場合でもピアから通知された初期ウィンドウサイズを確実にクライアント側のウィンドウ管理に反映させることが、この変更の目的です。初期ウィンドウサイズが正しく設定されないと、データ転送が滞ったり、パフォーマンスが低下したりする可能性があります。

前提知識の解説

SSH (Secure Shell)

SSHは、ネットワークを介してコンピュータを安全に操作するためのプロトコルです。主にリモートログインやファイル転送(SCP, SFTP)に利用されます。暗号化によって通信内容を保護し、認証によって正当なユーザーのみがアクセスできるようにします。

SSHチャネル (SSH Channel)

SSHセッション内では、複数の論理的な「チャネル」を開くことができます。各チャネルは独立したデータストリームであり、例えば、シェルセッション、ポートフォワーディング、X11転送など、異なる目的のために使用されます。チャネルは、SSHプロトコルにおける多重化のメカニズムを提供します。

SSHウィンドウサイズ (SSH Window Size) とフロー制御

SSHプロトコルは、TCPのような信頼性のあるストリームプロトコル上で動作しますが、独自のフロー制御メカニズムを持っています。これが「ウィンドウサイズ」です。

  • ウィンドウサイズ: 各チャネルにおいて、受信側が送信側に対して「これだけのバイト数なら追加で受け取れる」と通知する値です。送信側はこのウィンドウサイズを超えてデータを送信してはなりません。
  • フロー制御の目的: 受信側のバッファが溢れるのを防ぎ、ネットワークの輻輳を緩和し、効率的なデータ転送を保証します。
  • ウィンドウの更新: 受信側はデータを受信するたびに、残りのウィンドウサイズを計算し、必要に応じてSSH_MSG_CHANNEL_WINDOW_ADJUSTメッセージを送信して、送信側にウィンドウサイズを「補充」するよう通知します。

channelOpenConfirmMsg

これは、SSHプロトコルにおいて、クライアントがチャネル開設要求(SSH_MSG_CHANNEL_OPEN)を送信した後、サーバーがその要求を承認した際に返送するメッセージです。このメッセージには、サーバー側のチャネルID、初期ウィンドウサイズ、最大パケットサイズなどの情報が含まれます。

  • MyId: サーバー側で割り当てられたチャネルID。
  • MyWindow: サーバーがこのチャネルで最初に受け入れ可能なバイト数(初期ウィンドウサイズ)。
  • MyMaxPacketSize: サーバーがこのチャネルで受け入れ可能な最大パケットサイズ。

このMyWindowの値が、クライアントがサーバーにデータを送信する際の初期の許容量となります。

技術的詳細

このコミットの核心は、SSHチャネルの確立フェーズにおけるフロー制御の初期化です。

SSHチャネルが開かれる際、クライアントとサーバーはそれぞれ自身のチャネルID、初期ウィンドウサイズ、最大パケットサイズを相手に通知します。クライアントがSSH_MSG_CHANNEL_OPENを送信し、サーバーがSSH_MSG_CHANNEL_OPEN_CONFIRMATION(GoのコードではchannelOpenConfirmMsgとして表現されている)を返します。このchannelOpenConfirmMsgには、サーバーがそのチャネルで受け入れ可能な初期ウィンドウサイズ(MyWindow)が含まれています。

問題は、Goのexp/sshクライアント実装が、このchannelOpenConfirmMsgで通知されるMyWindowの値を適切に利用していなかった点にありました。一部のサーバーはMyWindowにゼロ以外の値を設定していましたが、別のサーバーはゼロを設定していました。クライアントがこの初期ウィンドウサイズを無視したり、適切に処理しなかったりすると、クライアントがサーバーにデータを送信する際に、サーバー側のバッファが準備できていないにもかかわらずデータを送りつけてしまうか、あるいは、サーバーが既にデータを受け入れる準備ができているにもかかわらず、クライアントがウィンドウサイズがゼロであると誤解してデータ送信を停止してしまう、といった問題が発生する可能性がありました。

このコミットは、channelOpenConfirmMsgを受信した際に、そのメッセージに含まれるMyWindowの値を、クライアント側のチャネルのウィンドウ管理機構(ch.winチャネル)に即座に送信することで、この初期ウィンドウサイズ広告が失われないように修正しています。これにより、クライアントはサーバーが最初に受け入れ可能なデータ量を正確に把握し、それに基づいてデータ送信を開始できるようになります。

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

変更はsrc/pkg/exp/ssh/client.goファイルの一箇所のみです。

--- a/src/pkg/exp/ssh/client.go
+++ b/src/pkg/exp/ssh/client.go
@@ -195,6 +195,7 @@ func (c *ClientConn) openChan(typ string) (*clientChan, error) {
 	switch msg := (<-ch.msg).(type) {
 	case *channelOpenConfirmMsg:
 		ch.peersId = msg.MyId
+		ch.win <- int(msg.MyWindow)
 	case *channelOpenFailureMsg:
 		c.chanlist.remove(ch.id)
 		return nil, errors.New(msg.Message)

コアとなるコードの解説

func (c *ClientConn) openChan(typ string) (*clientChan, error)関数は、新しいSSHチャネルを開く処理を担当しています。

この関数内で、switch msg := (<-ch.msg).(type)ブロックは、チャネル開設要求に対するサーバーからの応答メッセージを処理しています。

  • ch.msgは、サーバーから受信したチャネル関連のメッセージが送られてくるチャネルです。
  • case *channelOpenConfirmMsg:は、サーバーがチャネル開設を承認したことを示すchannelOpenConfirmMsgを受信した場合の処理です。

変更前のコードでは、channelOpenConfirmMsgを受信した際に、ピアのチャネルID (msg.MyId) はch.peersIdに設定されていましたが、ピアの初期ウィンドウサイズ (msg.MyWindow) は特に処理されていませんでした。

追加された行:

ch.win <- int(msg.MyWindow)

この行が、このコミットの主要な変更点です。

  • msg.MyWindow: channelOpenConfirmMsgに含まれる、サーバーがこのチャネルで最初に受け入れ可能なバイト数(初期ウィンドウサイズ)です。これはuint32型です。
  • int(msg.MyWindow): uint32型のMyWindowint型にキャストしています。
  • ch.win <- ...: ch.winは、このチャネルのウィンドウサイズを管理するためのGoチャネル(おそらくchan int型)です。このチャネルにmsg.MyWindowの値を送信することで、クライアント側のチャネル実装が、サーバーから通知された初期ウィンドウサイズを認識し、それに基づいてデータ送信のフロー制御を開始できるようになります。

これにより、サーバーがゼロ以外の初期ウィンドウサイズを通知した場合でも、その情報が適切にクライアントのウィンドウ管理に反映され、データ転送がスムーズに開始されるようになります。また、ゼロを通知した場合でも、そのゼロが正しく認識され、ウィンドウ調整メッセージが来るまでデータ送信が抑制されることになります。

関連リンク

参考にした情報源リンク