[インデックス 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
型のMyWindow
をint
型にキャストしています。ch.win <- ...
:ch.win
は、このチャネルのウィンドウサイズを管理するためのGoチャネル(おそらくchan int
型)です。このチャネルにmsg.MyWindow
の値を送信することで、クライアント側のチャネル実装が、サーバーから通知された初期ウィンドウサイズを認識し、それに基づいてデータ送信のフロー制御を開始できるようになります。
これにより、サーバーがゼロ以外の初期ウィンドウサイズを通知した場合でも、その情報が適切にクライアントのウィンドウ管理に反映され、データ転送がスムーズに開始されるようになります。また、ゼロを通知した場合でも、そのゼロが正しく認識され、ウィンドウ調整メッセージが来るまでデータ送信が抑制されることになります。
関連リンク
- Go CL 5372083: https://golang.org/cl/5372083
参考にした情報源リンク
- RFC 4254 - The Secure Shell (SSH) Connection Protocol (Section 5.2. Channel Open Confirmation)
- RFC 4254 - The Secure Shell (SSH) Connection Protocol (Section 5.2. Flow Control)
- SSH Flow Control - Stack Overflow
- SSH Channel Windowing - The FreeBSD Project
[インデックス 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
型のMyWindow
をint
型にキャストしています。ch.win <- ...
:ch.win
は、このチャネルのウィンドウサイズを管理するためのGoチャネル(おそらくchan int
型)です。このチャネルにmsg.MyWindow
の値を送信することで、クライアント側のチャネル実装が、サーバーから通知された初期ウィンドウサイズを認識し、それに基づいてデータ送信のフロー制御を開始できるようになります。
これにより、サーバーがゼロ以外の初期ウィンドウサイズを通知した場合でも、その情報が適切にクライアントのウィンドウ管理に反映され、データ転送がスムーズに開始されるようになります。また、ゼロを通知した場合でも、そのゼロが正しく認識され、ウィンドウ調整メッセージが来るまでデータ送信が抑制されることになります。
関連リンク
- Go CL 5372083: https://golang.org/cl/5372083