amitokiのL3中継プラグインと、独自プロトコルの実験・比較環境。動的経路・混雑通知・Web観測、プラグインの導入、設計と次の用途を参照する。
Ethernetの直上に独自ヘッダを載せ、短文の優先転送、受信側による送信量の制限、期限切れの破棄、2経路への複製を比較する試作。IP・TCP・UDPは使わない。Linuxのraw socketを使う別実行ファイル amitoki-l3 として実装している。
benchの既定は信頼性のある順序なし配送。通信グループごとに順序保証を選べる。信頼性配送の使い方と仕様を参照。従来の期限内ACK率の実験は--delivery deadlineで実行する。このページの送信枠・期限・比較値の説明はdeadlineモードを対象にする。
修正後の比較結果では、全60試行の完了と、混雑時の遅延・帯域・CPUの交換条件を確認した。
信頼性配送とTCPの比較は同じ負荷でL3とTCPを比べるを参照。順序なし・順序ありのL3と、TCPの1接続・2接続を全件配送後の遅延・速度で比べる。
まず「比較試験を実行する」を読めば試せる。後半はプロトコルと設定の参照用。ネットワークとRustの開発者を対象にしている。
Linux、Rust、Docker Engineを使う。Ubuntuでの導入コマンドは比較環境の準備を参照する。このcrate単体のビルドにはNode.jsとプラグインのsubmodule取得は不要。
リポジトリのルートで実行する。Dockerを実行できるユーザーで使う。
cargo test -p amitoki-l3-lab --locked
bash scripts/test-l3.shスクリプトがreleaseビルドと試験用イメージの作成を行い、外部非接続のコンテナを起動する。コンテナ内にだけvethと遅延設定を作り、終了時に削除する。イメージの初回ビルドではパッケージの取得にネットワーク接続を使う。
既定は期限付き配送18条件を各3秒・3回、その後に信頼性配送10条件を1回。短い確認は次で実行する。CIも同じ設定を使う。
L3_REPETITIONS=1 L3_DURATION_MS=1000 bash scripts/test-l3.sh artifacts/l3/quick出力先の既定値は artifacts/l3/<JST日付>/<時刻>/。実験ごとに新しいディレクトリを指定する。
| ファイル | 内容 |
|---|---|
report.md |
各回の期限内ACK率とRTT |
measurements.json |
全ノードの計測値と試験条件 |
verification.json |
成否、カーネル、実行ファイルのSHA256 |
reliable/verification.json |
信頼性配送10条件の検証結果 |
reliable/<条件>/deliveries.jsonl |
アプリへ渡したchannel・sequence・本文 |
<回>-<条件>/*.json |
ノード設定、起動通知、ノード別レポート |
<回>-<条件>/sample.pcap |
先頭128フレームまでの実通信 |
<回>-<条件>/*.log |
プロセスの出力 |
失敗時は各条件の .log を確認する。creditを取得できません は、受信側から送信許可が戻らなかったことを示す。ルーター不在の条件では、この失敗を確認する。再現情報を共有する場合は、Issueに条件と verification.json を添える。
flowchart LR
A[送信側 A] -->|a1 ↔ r1a| R1[ルーター R1]
R1 -->|r1b ↔ b1| B[受信側 B]
A -->|a2 ↔ r2a| R2[ルーター R2]
R2 -->|r2b ↔ b2| B
4プロセス・4組のvethを、1個のコンテナのネットワーク名前空間に置く。各vethにはIPアドレスを付けず、ブリッジも作らない。AとBの間には必ずRustのルーターが入る。別VMを4台使う構成ではない。
ルーターの各出力を1Mbpsに制限する。短文は128バイトを毎秒100件、背景負荷は1200バイトを毎秒500件。これは混雑を再現する条件であり、実装の最大帯域ではない。帯域計算はEthernetヘッダを含み、FCS・preamble・IFGは含まない。
| 条件 | 確かめること |
|---|---|
idle_fifo / idle_priority |
無負荷時のFIFOと優先制御 |
congested_fifo / congested_priority |
背景負荷がある場合の短文の遅延 |
delayed_single / delayed_dual |
主経路に30msの遅延がある場合の1経路と2経路 |
delayed_no_replica_budget |
複製帯域を0にすると複製されないこと |
primary_loss_dual |
主経路の送信を100%捨てても別経路で届くこと |
dual_duplicates |
同じ本文が2経路で届いても配送は1回であること |
receiver_limited |
受信側が毎秒20件に送信枠を制限すること |
retry_delayed |
8ms遅延・再送1回でも重複配送しないこと |
missing_router |
ルーターなしでは到達しないこと |
clock_offsets / clock_drift |
数秒の時計差と、相対400ppmの進み方の差があっても同期すること |
clock_asymmetric |
片方向1msの遅延を誤差区間に含めて通信すること |
clock_holdover / clock_recovery |
同期断で送信を止め、同期が戻ったら再開すること |
clock_too_uncertain |
誤差が上限を超える経路では期限付き通信を止めること |
主経路の遅延・欠落はR1からBへの片方向に設定する。逆方向は正常。2経路の分だけ利用できる資源も増えるため、帯域の等しい1経路との性能比較にはなっていない。
短文の期限は既定20ms、背景負荷は200ms。再送試験だけ短文を50msにする。期限内ACK率は、送信予定の全メッセージに対する成功率。送信枠不足、キューの満杯、期限切れ、ACKが戻らないものも分母に含む。RTTの開始は送信予定時刻なので、ローカルで待った時間も含まれる。p50/p99は期限内ACKが返ったメッセージだけの値であり、成功率と併せて読む。
試験は配送数、送信枠と複製帯域の上限、期限、重複処理、EtherType、hop数の減算を検査する。CIではホスト負荷で変わるp99の性能閾値は設けない。
JSONの benchmark.*.sent は、有効な送信枠を消費して転送処理へ渡したメッセージ数。後段のキューでの破棄も含む。socketから実際に送信できたフレーム数は network.sent に記録する。期限切れと不正な送信枠による拒否は別々に集計する。
- 時計同期を設定した場合は基準ノードとの時刻交換を行う。送信側が
REQUESTを送り、受信側が許可した件数をGRANTで返す。開始時の交換を終えてから計測する。 - 送信側が枠を1件消費し、メッセージID・期限・本文を持つ
DATAを出す。枠がなければその送信予定分を失敗に数える。 - ルーターは時計、期限、hop数を確認し、宛先と経路IDで次のリンクを選ぶ。各出力の有界キューと帯域制限を通して転送する。
- 受信側が枠と本文を検証する。最初の有効な到着だけを配送として数え、本文のfingerprintを
ACKで返す。 - 送信側は同じメッセージの正しいACKが期限内に戻った場合だけ成功に数える。
枠は送信元・起動ごとのsession・クラスに結び付ける。要求の再送や2経路からの同一要求は同じGRANTを返すので、許可数が増えない。短文と背景負荷には別々のtoken bucketを使う。受信側全体の毎秒の許可数を設定し、初期バーストは各32件まで。受信側が空き帯域を推定して自動調整するアルゴリズムは未実装。
優先キューは制御32件、短文32件、背景負荷128件分の空間を確保する。短文は期限が近いものから出す。制御が4件、短文が8件続いたら、待っている他クラスにも送信機会を与える。FIFO比較では同じ総容量192件を到着順で処理する。期限切れはどちらの方式でも送信前に除去する。短文の本文は最大256バイト、背景負荷は最大1400バイト。
2経路モードでは短文を複製する。同じID・枠・期限を保ち、到着順で採用する。重複には再度ACKを返す。再送は最大3回・5ms間隔で主経路を使い、複製と共通の帯域枠を消費する。既定は毎秒30,000バイト、初期バーストは最大フレーム2件分。制御要求の再送は別に5ms間隔で制限する。
配送履歴は最大8192件、発行済みGRANTは最大256件、GRANTあたり最大32枠。生存中の履歴を追い出さず、上限に達したら新規処理を拒否する。枠を使った記録はGRANTの期限まで保持し、配送履歴が期限切れになった後の再利用も拒否する。再起動するとこの状態は失われるため、再起動をまたぐ重複排除は保証しない。
EtherTypeはLocal Experimentalの 0x88B5、識別子は AMTK、プロトコルのversionは2。RFC 9542の実験用割当を使う。同じEtherTypeの別実験とはmagicで区別する。整数はnetwork byte order。
| オフセット | 長さ | フィールド |
|---|---|---|
| 0 | 4 | magic AMTK |
| 4 | 1 | version |
| 5 | 1 | kind: DATA=1 / ACK=2 / REQUEST=3 / GRANT=4 / SYNC_REQUEST=5 / SYNC_REPLY=6 |
| 6 | 1 | class: short=0 / bulk=1 |
| 7 | 1 | hops: 初期値16、ルーターごとに減算 |
| 8 / 12 | 各4 | source / destinationノードID |
| 16 / 24 | 各8 | session / message ID |
| 32 / 40 | 各8 | 期限のµs / 時計ドメイン |
| 48 | 8 | credit: 上位32bitはGRANT ID、下位32bitは枠番号または許可数 |
| 56 | 2 | 本文の長さ |
| 58 / 59 | 各1 | path / flags(bit0: 複製・再送、bit1: 混雑通知、bit2: ローカル滞留制限) |
| 60 | 2 | 予約領域、0固定 |
| 62 | 2 | ヘッダと本文のone's complement checksum |
| 64 | 8 | 試行ごとの送信時刻。ACK/NACKでechoする |
| 72 | 8 | 空き帯域の推定B/s |
| 80 / 84 | 各4 | 混雑ノードID / キュー待ちµs |
| 88 | 8 | 混雑ノードの出口設定帯域B/s |
ACKの本文は8バイトのFNV-1a fingerprint。REQUESTとGRANTには本文がない。checksumとfingerprintは破損検出・一致確認用で、送信元認証には使わない。
信頼性配送はkind=7〜13を使い、本文先頭16バイトにchannel・順序指定・受信世代を載せる。旧実行ファイルはこれらを解釈できないため、信頼性配送ではルーターを含む全ノードを更新する。deadlineモードも96バイトの共通ヘッダを使う。version 1とは互換性がない。
時計はLinuxの CLOCK_BOOTTIME。clock.authorityを全ノードに設定すると、独自L3の時刻交換で別VM・別マシンにも対応する。OS時計は変更しない。誤差が大きいときや同期の失効時は新しいDATA・OPENとdeadlineモードのACK・送信枠を止める。信頼性配送のACK・READY・RESETは同じ時計世代なら通し、session・受信epochを配送層で照合する。時計同期の設計と設定を参照。設定を省略した場合は同じboot ID・time namespaceの実験だけを許可する。期限が1秒を超えて先の通常パケットは拒否する。
JSON設定はノードID、リンクの相手MAC、宛先と経路ID、出力ごとの帯域を指定する。例はR1の設定。インターフェース自体は事前に作成しておく。
{
"node": 11,
"links": [
{"interface": "r1a", "peer_mac": "02:88:b5:00:00:01"},
{"interface": "r1b", "peer_mac": "02:88:b5:00:00:04"}
],
"routes": [
{"destination": 1, "path": 1, "interface": "r1a"},
{"destination": 2, "path": 1, "interface": "r1b"}
],
"scheduler": "priority",
"bytes_per_second": 125000,
"clock": {"authority": 2}
}ノードの起動にはraw socketを開く権限が必要。比較スクリプトはコンテナ内だけに NET_RAW と、veth・遅延設定用の NET_ADMIN を付ける。各サブコマンドの設定は次で確認できる。
cargo build --release -p amitoki-l3-lab --locked
target/release/amitoki-l3 router --help
target/release/amitoki-l3 receiver --help
target/release/amitoki-l3 bench --helpplugin/が本体SDKへのアダプタ、src/がL3の配送と比較CLI。補助プロセスがruntime::Endpointを使い、フレームの分割・再構成と重複排除を行う。起動・設定・配送契約を参照する。ソケット互換API、動的経路探索、暗号化・認証は含まない。
実装はAF_PACKETを使うユーザー空間のルーター。キューがある間と1ms未満の待機はbusy waitを使うため、CPU使用率は高くなる。物理NICの最大性能とAF_XDPは未検証。UDP/IPv4との比較と別VM試験では、同じEthernet上でLinux UDP socketと比較できる。単純転送の性能と、混雑時の期限内ACK率を分けて測る。
受信側の許可と短文優先はHoma、複製と重複排除はDetNetの設計を参考にしている。いずれのプロトコルの実装でもない。