概観

espress_streamは、実用性を維持した上で、必要最小限の機能を持つストリーム通信のプロトコルです。 また、それをできるだけ単純に実装したものでもあります。

必要最小限の機能は以下のように設定しました。

  • 接続指向
  • 順序保証
  • フロー制御
  • 再送制御
  • 輻輳制御
  • 誤り検出
  • 鍵交換
  • 暗号化
  • メッセージ認証

ネットワークインターフェイス層としては任意のデータグラム通信を想定しています。 linux用実装においてはこれをUDPとすることで、インターネット通信を可能にしています。

実装においては、組み込み機器を含む様々なアーキテクチャーへの対応を考慮し、以下のような要素を備えることとしました。

  • スレッド使用・未使用の選択可
  • ヒープ使用・未使用の選択可
  • 小規模で明確なアーキテクチャー依存部

アルゴリズムの要素技術

接続

接続は1RTT (ラウンドトリップタイム)で行なわれます。 この中で鍵交換が行なわれ、鍵が共有されます。

espress_streamではサーバー、クライアント認証は行なっていません。 サーバー、クライアント認証はアプリケーションが行なうことを前提としています。 接続パケットには任意のデータが搭載できるようになっています。 アプリケーションはこれを利用することで、サーバー、クライアント認証の処理を接続時に進めておくことができます。

切断

切断は1/2RTTで行なわれますが、これにより切断が行なわれるのは切断パケット送信側の送信チャンネルだけです。 espress_streamプロトコルでは、1セッション内の送受信チャンネル各々の切断は独立しておこなわれます。

切断がたった1つのパケット送信によって行なわれるために、無視できない確率で切断通知の不達が発生します。 espress_streamではある一定期間(現在の実装では10秒)データが受信されないと、その受信チャンネルは切断されたと認識することになっています。 これにより、切断パケットの受信がなくとも、受信側は受信チャンネルを能動的に切断することができます。

送信チャンネルの接続を維持したいが送信するデータがない場合はある一定間隔(現在の実装では1秒間隔)でゼロサイズのデータを送信することで切断を抑制します。

このような無受信期間タイムアウトによる切断検出と無送信データ期間のビーコン送信による活線維持は、espress_stream実装内で自動的に行なわれるので、アプリケーションは意識する必要はありません。

espress_streamではビーコン送信を含むデータの送信に対応する応答が一定時間(現在の実装では10秒)受信されないと、通信が途絶したと判断することも行っています。 これはTCPにおけるキープアライブ1の機能と同等のものです。 通信途絶は送信チャンネル側のエラーとして検出されますが、受信チャンネル側もエラーとします。 通信途絶は送信チャンネル側での検出のため、送信チャンネルが切断されている場合、検出されません。 もし、送信チャンネルが切断されている状態で通信途絶が発生した場合、受信チャンネルの切断として検出されます。

通信途絶の検出もespress_stream実装内で自動的に行われます。通信途絶の検出はアプリケーションに通知され、送受信等のAPI実行結果にエラーとして反映されます。

コネクションID

接続の識別はコネクションIDによってなされ、ネットワークアドレスには依存しません。

このため、espress_streamは一つのネットワークアドレスから一つのネットワークアドレスへの接続を同時に複数生成することができます。 一つのアドレスにサーバーとクライアントを共存させることも可能です。 ネットワーク接続のマイグレーションも可能です。

カウンター番号

接続パケット以外のパケットにはカウンター番号が付けられています。 カウンター番号はパケットが送信されるたびにインクリメントされます。 再送パケットにおいてもインクリメントされます。

これにより、送信パケットの一意性を保障し、計測RTTの正確性とパケットの保安性を高めます。

カウンター番号は32ビットの整数で、ラップアラウンドします。 このため、保安性に必要な一意性に問題が発生しますが、これはラップアラウンドするたびに鍵拡張アルゴリズムにより共有鍵を更新することで解決しています。

フロー制御

フロー制御はTCPと同じスライディングウィンドウ方式によって実現されています2。 ただし、パケットの単純化、実装の容易化を目的にセレクティブACK3は用いられていません。 遅延ACK2も同様に用いられていません。

現在の実装ではNagleアルゴリズム2は用いられていません。 送信バッファーにMTUを満たすサイズのデータが記録されないと実際のデータ送信が行なわないので、アプリケーションは適宜データの排出を指示する必要があります。

再送制御

TCPと同様の再送信タイムアウト2による再転送と高速再転送4を行ないます。

再送信タイムアウト時間もlinuxのTCP実装で用いられているのと同じアルゴリズムで、計測RTTから動的に求められています。

輻輳制御

輻輳ウィンドウを輻輳発生状態によって調整して制御しています4。 現在の実装で用いられている輻輳ウィンドウを調整するアルゴリズムはNew Reno5です。

New Renoは若干古い方式ですが、実装が簡単なため採用されました。

鍵交換

接続時に交換される公開鍵により双方のノードに共通の鍵を生成します。 鍵交換にはECDHアルゴリズム、楕円曲線にはCurve25519を使用しています6

鍵拡張

共有鍵から暗号とメッセージ認証のための鍵を生成します。 鍵拡張にはSHA2-2567を用いたHKDF関数8を使用しています。

接続時に送信パケットの暗号、メッセージ認証に用いる鍵拡張のためのソルトを両ノードが互いに送信します。 互いに送信するソルトは独立した乱数値です。 このため、各々のノードが送信するパケットには異なる鍵列が用いられます。

暗号化

暗号化は送信データおよびそのメタ情報に対して行なわれています。 暗号にはブロック暗号AES2569を暗号利用モードCTR10で使用しています。 CTRで用いるナンスには送信データパケットのヘッダーを使用しています。

メッセージ認証

全てのパケットにはメッセージ認証が付けられます。 メッセージ認証にはSHA2-2567を用いたHMAC11をETMで使用しています。 メッセージ認証によりパケットの偽造、誤りを検出します。

実装の要素技術

スレッド

スレッドの使用、未使用が選択できるようになっています。

スレッド使用時はposixにおけるpthread_tpthread_createpthread_joinに相当するstruct threadthread_createthread_joinが定義され、使用されます。 スレッド起動、停止は集中的に行われており、スレッド操作のオーバーヘッドを減らしています。 (linux用実装ではAES256-CTRの処理を行う部分にOpenMPを使用しています。 このため、espress_stream実装のソースコードで明示的に起動、停止されるスレッド以外のスレッドが起動、停止されています。)

スレッド使用時に生成されるスレッドからは、ライブラリー内の各種モジュールを作動させるルーチンが呼び出されます。 スレッド未使用時には、タイムアウトを持ち、処理の完了を待つAPIからこれらのルーチンが適宜呼び出されます。 このため、アプリケーションプログラマーは、大抵の場合、スレッド未使用時もスレッド使用時と変わらない感覚でAPIを使用できます。

ヒープ

ヒープの使用、未使用が選択できるようになっています。

ヒープ使用時は標準ライブラリーのmallocfreeが使用されます。 ヒープの確保は、前述のスレッドと同様、集中的に行われ、低効率のヒープに対しても性能低下が発生しにくくなっています。

ヒープ未使用時には、ライブラリーの設定値として、使用するネットワークインターフェイスとセッションの最大数を指定します。 各々の最大数に必要なメモリーはライブラリー内で静的に確保され、適宜使用されます。 このため、ヒープ未使用時においてもヒープ使用時とAPI使用方法が変わることはありません。

アーキテクチャー依存

特定のアーキテクチャーへの対応は基本実装に対して適宜モジュールを入れ替えることで実現します。

基本実装はlinux上で動作するように作られていますが、アーキテクチャー依存部は以下にしめす小規模なものになっています。

espress_stream_tick
チックカウンター部。
espress_stream_dgram
データグラム型ネットワーク通信部。基本実装ではUDPをソケットインターフェイスにより提供しています。

これらを動作させたいアーキテクチャー用のものに入れ替えることで任意のアーキテクチャー上でespress_streamを動作させることができます。

更に、必要に応じて以下のモジュールを入れ替えることで、システムに最適化することができます。

espress_stream_rand
真正乱数生成部。基本実装ではチックカウンターをシードとする簡易な疑似乱数生成器となっているので、実用する場合は入れ替えることが必須となります。
espress_stream_cond
同期機構部。posixのpthread_condに類似していますが、少し異なる機能を提供しています。 基本実装では疑似実装になっています。 スレッドを使用する場合はアーキテクチャーに合わせたものに入れ替える必要があります。
espress_stream_mutex
排他制御部。posixのpthread_mutexに相当し、機能的にもほぼ同じものです。 基本実装では疑似実装になっています。 スレッドを使用する場合はアーキテクチャーに合わせたものに入れ替える必要があります。

その他、espress_stream_block_cipher (ブロック暗号部)、espress_stream_hash (ハッシュ生成部)等に対してシステムに同等の機能が存在する場合、それらを利用するようなモジュールに入れ替えることで、より最適化することができる可能性があります。

参考文献

  1. R. Braden, “Requirements for Internet Hosts – Communication Layers”, RFC 1122, October 1989.
  2. Eddy, W., “Transmission Control Protocol (TCP)”, RFC 9293, August 2022.
  3. Mathis, M., Mahdavi, J., Floyd, S., and A. Romanow, “TCP Selective Acknowledgment Options”, RFC 2018, October 1996.
  4. Allman, M., Paxson, V., and E. Blanton, “TCP Congestion Control”, RFC 5681, September 2009.
  5. T. Henderson, S. Floyd, A. Gurtov, and Y. Nishida, “The NewReno Modification to TCP’s Fast Recovery Algorithm”, RFC 6582, April 2012.
  6. A. Langley, M. Hamburg, and S. Turner, “Elliptic Curves for Security”, RFC 7748, January 2016.
  7. D. Eastlake 3rd, and T. Hansen, “US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)”, RFC 6234, May 2011.
  8. H. Krawczyk, and P. Eronen, “HMAC-based Extract-and-Expand Key Derivation Function (HKDF)”, RFC 5869, May 2010
  9. National Institute of Standards, “Advanced Encryption Standard (AES)”, Federal Information Processing Standards Publication 197, November 2001.
  10. Morris Dworkin, “Recommendation for Block Cipher Modes of Operation”, NIST Special Publication 800-38A 2001 Edition, December 2001.
  11. H. Krawczyk, M. Bellare, and R. Canetti, “US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)”, RFC 2104, February 1997

INDEX