PyTCP
Python で書かれた TCP/IP スタック
PyTCP は、Python で書かれた完全に機能する TCP/IP スタックです。スライディング ウィンドウ メカニズムと基本的な輻輳制御に基づいた信頼性の高いパケット配信により、TCP ストリームベースのトランスポートをサポートします。 SLAAC アドレス構成を使用した IPv6/ICMPv6 プロトコルもサポートします。これは、Linux TAP インターフェイスに接続されたユーザー空間プログラムとして動作します。シンプルなルーティングが実装されており、ローカル ネットワークおよびインターネット経由でトラフィックを送受信できます。
バージョン 2.7 には、以前のバージョンとは異なり、PyTCP スタック コードがライブラリの形式で含まれているため、外部コードから簡単にインポートして使用できます。これにより、ユーザー エクスペリエンスがよりスムーズになり、最終的にはサードパーティ アプリケーションで標準 Linux スタック呼び出し (ソケット ライブラリなど) を PyTCP 呼び出しに置き換える完全な機能が提供されるようになります。
このプロジェクトは当初、Facebook でのネットワーク エンジニアの役割への準備の一環として、私の Python スキルを向上させ、ネットワークの知識をリフレッシュすることを目的とした純粋な教育的取り組みとして始まりました。それ以来、それは私がやや不定期に自分の時間を捧げる「ペットプロジェクト」のようなものになりました。ただし、通常は 1 ~ 2 か月ごとにいくつかの更新が追加されます。
ネットワーク プログラミングに興味のある方からの貢献と支援を歓迎します。あらゆるご意見をお待ちしております。また、一部のスタック機能は部分的にのみ実装される場合があることに注意してください (スタック操作に必要な場合)。これらは、次善の方法で実装されているか、(時間がないため) RFC に 100% 準拠していない方法で実装されている可能性があります。あるいは、まだ修正する必要があるバグが含まれている可能性があります。
私の他の 2 つの関連プロジェクトもお気軽にチェックしてください。
- RusTCP - Rust で PyTCP 機能の一部を書き直し、それを使用して IPv6/SRv6 ラボ ルーターを作成することを試みます。
- SeaTCP - C およびアセンブリ言語を使用して低遅延スタックの作成を試みます。
動作原理と試験セットアップ
PyTCP スタックは Linux TAP インターフェイスに依存します。 TAP インターフェイスは、ネットワーク側で、Linux ブリッジまたは Open vSwitch を介して既存の仮想ネットワーク インフラストラクチャに「接続」できる仮想インターフェイスです。内部側では、プログラムでパケットを送受信することで、TAP インターフェイスを他の NIC と同様に使用できます。
ローカル ネットワークで PyTCP スタックをテストしたい場合は、Linux カーネル (基本的に Linux OS) と PyTCP スタックの両方をローカル ネットワークに同時に接続できるようにする次のネットワーク設定を作成することをお勧めします。 。
<INTERNET> <---> [ROUTER] <---> (eth0)-[Linux bridge]-(br0) <---> [Linux TCP/IP stack]
|
|--(tap7) <---> [PyTCP TCP/IP stack]
サンプル プログラム (クライアントまたはサービス) がスタックを開始すると、API インターフェイスのような簡素化された BSD ソケットを介してスタックと通信できます。 PacketHandler
クラスから_*_phtx()
メソッドの 1 つを呼び出して、パケットを直接送信することもできます。
GitHub リポジトリからの PyTCP のクローン作成
ほとんどの場合、このタイプのインストールでは完全な開発およびテスト環境が提供されるため、PyTCP は GitHub リポジトリから直接複製する必要があります。
git clone http://github.com/ccie18643/PyTCP
クローン作成後、含まれているサンプルのいずれかを実行できます。
- スタック ルート ディレクトリ (「PyTCP」と呼ばれます) に移動します。
- 必要に応じて、
sudo make bridge
コマンドを実行して「br0」ブリッジを作成します。 -
sudo make tap
コマンドを実行して、Tap7 インターフェイスを作成し、それを「br0」ブリッジに割り当てます。 -
make
コマンドを実行して、開発とテストに適切な仮想環境を作成します。 - 走る
. venv/bin/activate
コマンドを使用して仮想環境をアクティブ化します。 - 任意の例 (例:
example/run_stack.py
を実行します。 - Ctrl+C を押して停止します。
さまざまなスタック動作パラメータを微調整するには、それに応じてpytcp/config.py
ファイルを編集してください。
PyPi リポジトリからの PyTCP のインストール
PyTCP は、PyPi リポジトリから通常のモジュールとしてインストールすることもできます。
python -m pip install PyTCP
インストール後、TAP インターフェイスが動作し、ブリッジに追加されていることを確認してください。
sudo ip tuntap add name tap7 mode tap
sudo ip link set dev tap7 up
sudo brctl addbr br0
sudo brctl addif br0 tap7
次のコードを使用して、PyTCP スタックをインポートして開始できます。スタック サブシステムを起動し、DHCPv4 と IPv6 SLAAC をそれぞれ使用して、IPv4 と IPv6 の両方のプロトコル アドレスを自動構成します。
from pytcp import TcpIpStack
stack = TcpIpStack ( interface = "tap7" )
stack . start ()
スタック サブシステムは独自のスレッドで実行されます。開始後、スタックは制御をユーザー コードに戻し、次の呼び出しを使用して停止できます。
特徴
すでに実装されています:
- Stack - 「ゼロコピー」アプローチを使用した高速パケットパーサー。
- Stack - 「ゼロコピー」アプローチを使用した高速パケット アセンブラ。
- スタック - MACアドレス操作ライブラリ - バッファプロトコル(Memoryview)に対応。
- スタック - IPv4アドレス操作ライブラリ - バッファプロトコル(Memoryview)と互換性があり、Python標準ライブラリに依存しません。
- スタック - IPv6アドレス操作ライブラリ - バッファプロトコル(Memoryview)と互換性があり、Python標準ライブラリに依存しません。
- コード -一部のライブラリとモジュールの単体テスト (Facebook の Testslide フレームワークに基づく)
- イーサネット プロトコル -イーサネット II 標準フレームのサポート。
- イーサネット プロトコル -ユニキャスト、IPv4 マルチキャスト、IPv6 マルチキャスト、およびブロードキャスト アドレッシング。
- ARP プロトコル -応答、クエリ、ARP キャッシュ メカニズム。
- ARP プロトコル - ARP プローブ/アナウンスメント IP 競合検出 (ACD) メカニズム。
- IPv4 プロトコル -デフォルトのルーティング。スタックは、IPv4 プロトコルを使用してインターネット経由でホストと通信できます。
- IPv4 プロトコル - DHCPv4 プロトコルを使用した自動 IPv4 アドレス構成。
- IPv4 プロトコル -インバウンド パケットの最適化、順序が乱れているデータ フラグメントや重複するデータ フラグメントを処理できる堅牢なメカニズム。
- IPv4 プロトコル -送信パケットの断片化。
- IPv4 プロトコル - IPv4 オプションは受け入れられますが、サポートされていません。
- IPv4 プロトコル -複数のスタックの IPv4 アドレスがサポートされており、それぞれが別の VRF に割り当てられたかのように動作します。
- ICMPv4 プロトコル -エコー要求、エコー応答、およびポート到達不能メッセージ。
- IPv6 プロトコル -デフォルトのルーティング。スタックは、IPv6 プロトコルを使用してインターネット経由でホストと通信できます。
- IPv6 プロトコル - EUI64 と重複アドレス検出を使用した自動リンクローカル アドレス構成。
- IPv6 プロトコル -ルーター アドバタイズメント / EUI64 を使用した自動 GUA アドレス構成。
- IPv6 プロトコル -要請ノード マルチキャスト アドレスの自動割り当て。
- IPv6 プロトコル - IPv6 マルチキャスト MAC アドレスの自動割り当て。
- IPv6 プロトコル -インバウンド パケットの最適化、順序が乱れているデータ フラグメントや重複するデータ フラグメントを処理できる堅牢なメカニズム。
- IPv6 プロトコル -送信パケットの断片化。
- ICMPv6 プロトコル -エコー要求、エコー応答、およびポート到達不能メッセージ。
- ICMPv6 プロトコル -近隣探索、重複アドレス検出。
- ICMPv6 プロトコル -近隣探索キャッシュ メカニズム。
- ICMPv6 プロトコル -マルチキャスト リスナー検出 v2 (MLDv2) プロトコルの実装 (スタックが必要とするメッセージのみ)。
- UDP プロトコル -完全サポート。スタックは、UDP プロトコルを使用して他のホストとデータを交換できます。
- UDP ソケット -完全サポート、Berkeley ソケットに似たスタックの「エンド ユーザー」 API。
- UDP サービス -テスト目的で実装された Echo、Discard、および Daytime サービス (「例」内)。
- TCP プロトコル - TCP 有限状態マシンの完全な実装。この時点で、スタックは TCP プロトコルを介して他のホストと大量のデータを交換できるようになります。
- TCP プロトコル - TCP オプションのサポート: MSS、WSCALE、SACKPERM、TIMESTAMP。
- TCP プロトコル -データ再送信を備えた TCP スライディング ウィンドウ メカニズム (高速再送信と時間ベースのシナリオ)。
- TCP プロトコル - TCP バックオフ メカニズム/基本的な輻輳制御。
- TCP プロトコル - TCP SYN/FIN パケットの再送信。
- TCP ソケット -完全サポート、Berkeley ソケットに似たスタックの「エンド ユーザー」 API
実装するには:
例
いくつかの ping パケットと 2 匹のサルが、IPv6 プロトコル上の TCP 経由で配信されました。
IPv6 近隣探索/重複アドレス検出/アドレス自動設定。
- スタックはリンクローカル アドレスの自動構成を試みます。 EUI64アドレスとして生成します。 DAD プロセスの一部として、適切な要請ノード マルチキャスト グループに参加し、生成されたアドレスに対して近隣要請を送信します。
- スタックは、生成したアドレスの近隣通知を受信しないため、そのアドレスをそのインターフェイスに割り当てます。
- スタックは、事前構成された静的アドレスの割り当てを試みます。 DAD プロセスの一部として、適切な要請ノード マルチキャスト グループに参加し、静的アドレスの近隣要請を送信します。
- 同じアドレスがすでに割り当てられている別のホストは、近隣通知メッセージで応答します。これは、割り当てようとしているアドレスが別のホストによってすでに割り当てられているため、スタックはそのアドレスを使用できないことをスタックに伝えます。
- スタックはルーター要請メッセージを送信して、使用する必要があるグローバル プレフィックスがあるかどうかを確認します。
- ルーターは、追加のプレフィックスを含むルーター アドバタイズメントで応答します。
- スタックは、受信したプレフィックスと EUI64 ホスト部分に基づいて生成されたアドレスの割り当てを試みます。 DAD プロセスの一部として、適切な要請ノード マルチキャスト グループに参加し、静的アドレスの近隣要請を送信します。
- スタックは、生成したアドレスの近隣通知を受信しないため、そのアドレスをそのインターフェイスに割り当てます。
- すべてのアドレスが割り当てられた後、スタックは、リッスンするすべてのマルチキャスト アドレスをリストしたマルチキャスト リスナー レポートをもう 1 つ送信します。
TX パケットが失われた後、TCP 高速再送信が動作中。
- シミュレートされたパケット損失メカニズムにより、発信パケットが「失われた」。
- ピアはパケットの SEQ 番号の不一致に気づき、「高速再送信要求」を送信します。
- スタックはリクエストを受信し、失われたパケットを再送信します。
RX パケット損失イベント中に動作中の順序が狂ったキュー
- シミュレートされたパケット損失メカニズムにより、受信パケットが「失われた」。
- スタックは受信パケットの SEQ 番号の不一致に気づき、「高速再送信」リクエストを送信します。
- ピアはリクエストを受信する前に、スタックが期待するよりも高い SEQ を持つ複数のパケットを送信します。スタックはそれらすべてのパケットをキューに入れます。
- ピアは失われたパケットを再送信します。
- スタックは失われたパケットを受信し、アウトオブオーダーキューに格納されているすべてのパケットをプルして処理します。
- スタックは、キューから取得された最新のパケットを確認するために ACK パケットを送信します。
TCP 有限状態マシン - スタックは TCP エコー サービスを実行しています。
- ピアが接続を開きます。
- ピアがデータを送信します。
- スタックはデータをエコーバックします。
- ピアが接続を閉じます。
TCP 有限状態マシン - スタックは TCP Echo クライアントを実行しています。
- スタックが接続を開きます。
- スタックがデータを送信します。
- ピアはデータをエコーバックします。
- スタックは接続を閉じます。
事前解析パケットの健全性チェックが実行されます。
- 最初のスクリーンショットは、健全性チェックがオフになっているスタックを示しています。不正な形式の ICMPv6 パケットによりクラッシュする可能性があります。
- 2 番目のスクリーンショットは、健全性チェックがオンになっているスタックを示しています。不正な形式の ICMPv6 パケットは、ICMPv6 プロトコル パーサーに渡される前に破棄されます。
- 3 番目のスクリーンショットは、不正な形式のパケットを示しています。パケットにレコードが 1 つだけ含まれている場合でも、MA レコード数フィールドは 777 に設定されています。
ARP プローブ/アナウンス メカニズム。
- スタックは ARP プローブを使用して、設定されているすべての IP アドレスについて競合の可能性を見つけます。
- IP アドレスの 1 つ (192.168.9.102) はすでに使用されているため、スタックはそれについて通知を受けてスキップします。
- 残りの IP アドレスは空いているため、スタックはそれぞれの IP アドレスに ARP アナウンスを送信してそれらを要求します。
ARP 解決と ping パケットの処理。
- ホスト 192.168.9.20 はスタックへの ping を試行します。これを行うには、まず ARP 要求パケットを送信してスタックの MAC アドレスを見つけます。
- スタックは、ARP Reply パケットを送信することで応答します (スタックは、ホストの要求からホストの MAC をすでに記録しているため、要求を送信する必要はありません)。
- ホストが ping パケットを送信し、スタックがそれに応答します。
IPの断片化。
- ホストは、3 つのフラグメント化された IP パケット (3 つのフラグメント) を使用して 4Kb UDP データグラムを送信します。
- スタックはパケットを受信して 1 つの部分に組み立て、それを (UDP プロトコル ハンドラーと UDP ソケット経由で) UDO Echo サービスに渡します。
- UDP Echo サービスはデータを取得し、UDP ソケットに戻します。
- UDP データグラムは IP プロトコル ハンドラーに渡され、IP パケットが作成され、リンクを超えていることが確認された後、MTU によって 3 つの個別の IP パケットに分割されます。
- IP パケットはイーサネット フレームにカプセル化され、TX リング上に置かれます。