3. 概要
P4は、ハードウェア/ソフトウェアスイッチ、ネットワークインターフェースカード、 ルーター、あるいはネットワーク・アプライアンスといった、 プログラマブルな転送要素のデータプレーンによって、 パケットがどのように処理されるかを記述するための言語です。 P4という名称は、この言語を紹介した元の論文である"Programming Protocol-independent Packet Processors"(プロトコル非依存のパケットプロセッサのプログラミング、 https://arxiv.org/pdf/1312.1719.pdf )に由来しています。 P4は当初スイッチのプログラミング用に設計されましたが、 その範囲は多種多様なデバイスをカバーするように拡張されています。 以降、このドキュメントでは、これらすべてのデバイスを指す一般的な用語として「ターゲット」を使用します。
多くのターゲットはコントロールプレーンとデータプレーンの両方を実装しています。 P4は、ターゲットのデータプレーン機能のみを記述するよう設計されています。 P4プログラムは、コントロールプレーンとデータプレーンが通信するインターフェースも部分的に定義しますが、 ターゲットのコントロールプレーン機能を記述するためにP4を使用することはできません。 以降、このドキュメントでは、私達がP4で「ターゲットをプログラミングする」と表現する場合、 それは「ターゲットのデータプレーンをプログラミングする」ことを意味します。
ターゲットの具体的な例として、図1は従来の固定機能スイッチとP4プログラマブルスイッチの違いを示しています。 従来のスイッチでは、製造元がデータプレーンの機能を定義します。 コントロールプレーンはテーブル(例: ルーティングテーブル)のエントリを管理したり、 専用のオブジェクト(例: メーター)を設定したり、 制御パケット(例: ルーティングプロトコルパケット)やリンク状態の変化や学習通知などの非同期イベントを処理したりすることによって、 データプレーンを制御します。
P4プログラマブルスイッチは従来のスイッチとは2つの本質的な点で異なります。
- データプレーンの機能は事前に固定されておらず、P4プログラムによって定義されます。 データプレーンは初期化時に、P4プログラムで記述された機能を実装するように設定され(長い赤色の矢印で示されています)、 既存のネットワークプロトコルに関する組み込みの知識を一切持っていません。
- コントロールプレーンは固定機能デバイスと同じチャンネルを用いてデータプレーンと通信します。 しかし、データプレーン内のテーブルやその他のオブジェクトのセットは、P4プログラムによって定義されるため、もはや固定されていません。 コントロールプレーンがデータプレーンとの通信に使うAPIはP4コンパイラによって生成されます。
このように、P4はプロトコル非依存であると言えますが、 これによってプログラマーは豊富なプロトコルのセットや、 その他のデータプレーンの動作を表現することが可能になります。
P4言語によって提供される中核的な抽象化は以下の通りです。
- ヘッダ型(Header types) はパケット内の各ヘッダのフォーマット(フィールドのセットとそれらのサイズ)を記述します。
- パーサー(Parsers) は受信パケット内で許可されるヘッダの順序、それらのヘッダの順序を識別する方法、 およびパケットから抽出するヘッダとフィールドを記述します。
- テーブル(Tables) はユーザー定義のキーとアクションを関連付けます。 P4テーブルは従来のスイッチテーブルを一般化したもので、ルーティングテーブル、フロールックアップテーブル、 アクセスコントロールリスト、その他のユーザー定義のテーブルタイプ(複雑な多変数決定を含む)を実装するために使用できます。
- アクション(Actions) はパケットのヘッダフィールドとメタデータがどのように操作されるかを記述するコード断片です。 アクションには、実行時にコントロールプレーンから提供されるデータを含めることができます。
- マッチアクションユニット(Match-action units) は以下の連続した操作を実行します。
- パケットフィールドまたは計算されたメタデータからルックアップキーを構築する。
- 構築されたキーを使用してテーブルルックアップを実行し、実行するアクション(関連するデータを含む)を選択する。
- 最後に、選択されたアクションを実行する。
- コントロールフロー(Control flow) はターゲット上でのパケット処理を記述する命令型プログラムであり、 データに依存したマッチアクションユニットの呼び出しシーケンスを含みます。デパーシング(パケットの再構築)も、コントロールフローを使用して実行できます。
- 外部オブジェクト(Extern objects) はアーキテクチャ固有の構成要素であり、明確に定義されたAPIを通じてP4プログラムから操作できますが、 その内部動作はハードワイヤード(固定)であり(例: チェックサムユニット)、したがってP4を用いてプログラムすることはできません。
- ユーザー定義メタデータ(User-defined metadata) は各パケットに関連付けられる、ユーザーが定義したデータ構造です。
- 組み込みメタデータ(Intrinsic metadata) は各パケットに関連付けられるアーキテクチャから提供されるメタデータ(例: パケットが受信された入力ポート)です。
図2は、P4を用いてターゲットをプログラミングする際の典型的なツールワークフローを示しています。
ターゲットの製造元は、ハードウェアまたはソフトウェアの実装フレームワーク、アーキテクチャ定義、およびそのターゲット用のP4コンパイラを提供します。 P4プログラマーは、特定のアーキテクチャ向けのプログラムを作成します。 このアーキテクチャは、ターゲット上にあるP4でプログラム可能なコンポーネントのセットと、それらの外部データプレーンインターフェースを定義するものです。
P4プログラムのセットをコンパイルすると、以下の2つの成果物が生成されます。
- 入力プログラムに記述された転送ロジックを実装するデータプレーン設定
- コントロールプレーンからデータプレーンオブジェクトの状態を管理するためのAPI
P4はプログラマブル・ネットワークインターフェースカード、FPGA、ソフトウェアスイッチ、 ハードウェアASICといった多種多様なターゲット上で実装できるように設計されたドメイン固有言語です。 そのため、P4言語はこれら全てのプラットフォーム上で効率的に実装できる構成要素のみに制限されています。
テーブル検索操作や外部オブジェクトとのやりとりにかかるコストを一定と仮定した場合、 全てのP4プログラム(すなわち、パーサーとコントロールブロック)は受信・解析される入力パケットの各バイトあたりに一定回数の操作を実行します。 パーサーはループを含むことがありますが、各サイクルで何らかのヘッダが抽出される限りにおいて、パケット自体が総実行回数に上限を与えます。 言い換えれば、これらの仮定のもとでは、P4プログラムの計算量は、全ヘッダの合計サイズに対して線形であり、データ処理中に蓄積された状態のサイズ(例: フロー数や処理された総パケット数)には決して依存しません。 これらの保証は、多種多様なターゲットで高速なパケット処理を可能にするための必要条件です(ただし十分条件ではありません)。
ターゲットのP4準拠性(P4 conformance)は、以下のように定義されます。
もし特定のターゲットTがP4プログラミング言語のサブセット(仮にP4Tとします)しかサポートしていない場合、そのターゲット上で実行されるP4Tで書かれたプログラムは、このドキュメントに記述されているものと全く同じ動作を提供しなければなりません。なお、P4に準拠したターゲットは任意のP4言語拡張やextern要素を提供することができます。
最先端のパケット処理システム(たとえば、専用ハードウェア上にマイクロコードを 記述するもの)と比べると、P4は次のような重要な利点を提供します。
- 柔軟性(Flexibility): P4では多くのパケット転送ポリシーをプログラムとして 記述できます。これは、固定機能の転送エンジンだけをユーザーに公開する従来の スイッチとは対照的です。
- 表現力(Expressiveness): P4は、汎用的な演算とテーブルルックアップだけを 用いて、高度でハードウェアに依存しないパケット処理アルゴリズムを表現できます。 このようなプログラムは、同じアーキテクチャを実装するハードウェアターゲット間で ポータブルです(十分な資源が利用可能であると仮定します)。
- 資源の割り当てと管理(Resource mapping and management): P4プログラムは、記憶資源を抽象的に記述します(例: IPv4送信元アドレス)。 コンパイラは、このようなユーザー定義フィールドを利用可能なハードウェア資源に 対応付け、割り当てやスケジューリングのような低レベルの詳細を管理します。
- ソフトウェア工学(Software engineering): P4プログラムは、型検査、 情報隠蔽、ソフトウェア再利用といった重要な利点を提供します。
- コンポーネントライブラリ(Component libraries): 製造元が提供する コンポーネントライブラリを使うことで、ハードウェア固有の機能を、ポータブルな 高水準のP4構成要素としてラップできます。
- ハードウェアとソフトウェアの発展の分離(Decoupling hardware and software evolution): ターゲットの製造元は、抽象アーキテクチャを用いることで、 低レベルなアーキテクチャ詳細の発展を高レベルな処理からさらに分離できます。
- デバッグ(Debugging): 製造元は、P4プログラムの開発およびデバッグを支援 するために、アーキテクチャのソフトウェアモデルを提供できます。
P414は以前の言語バージョンです。 これと比較するとP416では言語の構文と意味論に後方互換性のない重要な変更が数多く加えられています。 以前のバージョン(P414)から現在のバージョン(P416)への進化を図3に示します。 特にカウンタやチェックサムユニット、メーターなど多くの言語機能が言語から削除され、ライブラリに移されました。
その結果、この言語は70を超えるキーワードを持つ複雑な言語から比較的小規模なコア言語へと変わりました。 コア言語のキーワードは40未満です。 詳細はP4予約キーワードを参照してください。 ほとんどのP4プログラムを記述するために必要な基本構成要素のライブラリを伴います。
P4 v1.1ではライブラリ要素を記述できるexternという言語構成要素が導入されました。
v1.1の言語仕様で定義された多くの構成要素はこのようなライブラリ要素に変換されます。
これにはカウンタやメーターなど言語から削除された構成要素も含まれます。
一部の外部オブジェクトは標準化されることが期待されています。
これらはP4要素の標準ライブラリを記述する将来の文書の対象となります。
本書ではextern構成要素の例をいくつか示します。
P416ではアーキテクチャのプログラム可能な部分を記述するためにv1.1の言語構成要素の一部が導入されています。
これらの用途も変更されています。
対象となる言語構成要素はparser・state・control・packageです。
P416の言語改訂では安定した言語定義の提供が重要な目標の1つです。 P416で記述されたすべてのプログラムが将来の言語バージョン向けに扱われても、構文的な正しさと同一の動作を維持することを目指しています。 さらに将来の言語バージョンで後方互換性を損なう必要が生じた場合には、P416プログラムを新しいバージョンへ容易に移行する手段を提供するよう努めます。