P4PI: Raspberry Pi で P4言語を試す

# P4PI: Raspberry Pi で P4言語を試す

P4Piは、コンピュータネットワークの教育と研究のための低コストのオープンソースプラットフォームです。
このプラットフォームは、Raspberry Pi ボードをベースにしており、P4プログラミング言語を使用します。

## インストール

P4Piイメージを書き込む microSDカードを準備します。

P4Piリポジトリから最新のP4Piイメージをダウンロードします。
最新のリリースは[こちら](https://github.com/p4lang/p4pi/releases)から入手できます。"Assets"をクリックして、zipファイルをダウンロードします。

[Operating System] --> [Use custum]を選択し、ダウンロードしたP4Piイメージファイルをポイントします。

![Operating_System-use_custom.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1030733/f1476233-7d91-d4c1-86d7-ccca15506f68.png)

[Storage]ウィンドウでmicroSDを選択します。

![Storage-micro_sdmedia.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1030733/71ee94bd-5c51-3ae8-cd86-312ecf72d0e0.png)

[WRITE]を選択し、プロセスを開始します。

![RPi-Write.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1030733/30559d7d-367d-4a5f-5e96-d24a7931f71a.png)

書き込みの完了後、SDカードをRaspberry Pi ボードに装着します。

## P4PI デザイン

以下に、P4Piの設計における主要コンポーネントについての技術的な説明をします。最初のP4Piリリースでは、ベースハードウェアプラットフォームとしてRaspberry Pi 4 モデルBを使用しています。コンパイラとソフトウェアスイッチの場合、P4PiはオープンソースのT4P4Sフレームワークを拡張して使用します。これには、Raspberry Pi 固有のコードを隠すための抽象化レイヤーライブラリが含まれます。
Raspberry Pi 4 Model B にはクアッドコアARM64プロセッサが付属しており、2GB、4GB、または8GBのRAMで構成されています。
このプロトタイプは、4GB構成でテストされています。このデバイスには、有線ギガビットイーサネットポートとオンボードワイヤレスネットワーク(2.4GHzと5GHzの両方)も含まれているため、デバイスをワイヤレスアクセスポイントまたは単純なWiFiルーターとして機能させることができます。
下図は、P4Piアーキテクチャの概要を示しています。

![P4PI-Architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1030733/d4c6927a-019b-2b23-4504-1df1491818e2.png)

図が示すように、アーキテクチャは高速パスと低速パスに分けられます。高速パスでは、2つのCPUコアが分離され、パケット処理パイプラインの実行専用になっています。これらの2つのコアは、ギガビットインターフェイスとワイヤレスインターフェイスの2つのポートから到着するすべてのトラフィックを受信します。ワイヤレスインターフェイスはアクセスポイントモードで設定され、管理トラフィックを除くこのインターフェイスで受信されるすべてのトラフィックは、パケット処理パイプラインを介してブリッジされます。低速パスでは、残りの2つのコアが、オペレーティングシステム、P4Runtimeサーバー、および必要に応じてローカルコントロールプレーンアプリケーションの実行に使用されます。

## P4Pi との接続

P4Piの変更されていないイメージを使用する場合、P4Piをオンにするとすぐに、ホットスポットとして表示されます。
ワイヤレスネットワーク名(SSID)は p4pi です。初期パスワードは、raspberry です。セキュリティの脆弱性を回避するためにパスワードを変更する必要があります。
sshまたは Webインターフェースのいずれかを使用してP4Piを構成することが可能です。

### SSH接続

sshとポート22を使用してP4Piに接続することが可能です。デフォルトのユーザーは、piです。初期パスワードは、raspberryです。
passwdコマンドを使用して、最初のログイン後にパスワードを変更する必要があります。

```
ssh pi@192.168.4.1
```

P4Piには、/root/t4p4s/examplesフォルダーに格納されているすぐに使用できる多数のP4サンプルが含まれています。次のように、sshを介して既存のP4サンプルプログラムを実行できます。

```
sudo -i
echo l2switch > /root/t4p4s-switch
systemctl restart t4p4s.service
```

ここで、l2switchは P4プログラムの名前です。T4P4S systemdサービスは、t4p4s-startコマンドを実行します。このコマンドは、T4P4Sの P4プログラム例の名前を/root/t4p4s-switchから読み取り、コンパイルして実行します。

## P4 プログラムのカスタム化

T4P4SでP4プログラムを実行するには、最初に /root/t4p4s/examplesフォルダーにP4ファイルを追加する必要があります。
次に、/root/t4p4s/examples.cfg 構成ファイルに行を追加する必要があります。

```
l2switch    arch=dpdk hugepages=1024 model=v1model smem vethmode pieal piports
```

デフォルトでは、すべてのT4P4Sサンプルプログラムは、piealおよびpiportsで指定されたEALオプションを使用します。これらのオプションは、/root/t4p4s/opts_dpdk.cfgで編集できます。

## 実行例

### ARP and ICMP responder

この例では、次の図に示すようにP4Piセットアップを使用します。

![l2switch_setupA.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1030733/9dd46145-7d17-dce8-0641-8636336c4670.png)

P4プログラムは、イーサネット、ARP、IPv4、およびICMP(エコー要求)ヘッダーを解析できます。パイプラインは、ARPレスポンダーとICMPエコーレスポンダー機能を実装する2つのテーブルで構成されています。

最初の機能arp_exactは、ARP要求メッセージの宛先IPアドレス(IPv4)フィールドに完全一致を適用するテーブルによって実装されます。一致する場合、アクションarp_replyは、アクションパラメータとして指定されたMACアドレスで満たされたARP応答メッセージを生成し、イーサネットの送信元アドレスと宛先アドレスを交換して、パケットを発信元に送り返します。テーブルミスの場合、パケットは単にドロップされます。

2番目の機能icmp_responderは、複合キーで構成されるテーブルによって実装されます。完全一致の種類を持つイーサネットヘッダーの宛先MACアドレスと、LPM一致の種類を持つ宛先IPアドレスです。ICMPレスポンダーは、宛先IPアドレスの単純な完全一致テーブルを操作できることに注意してください。実例として複合キーを追加しました。一致する場合、テーブルicmp_responderはアクションicmp_replyを適用します。これは、着信パケットをICMPエコー応答に変換し、IPヘッダーのIPアドレスとイーサネットヘッダーのMACアドレスの両方を交換します。送信元と宛先のIPアドレスを交換するため、IPチェックサムを再計算する必要があります。ただし、ICMPヘッダーには、ヘッダーの内容が変更されるたびに再計算する必要があるチェックサムフィールドも含まれています。ICMPチェックサムの計算にはペイロードバイトも必要であるため、ICMPヘッダーにはパディングと呼ばれるvarbitフィールドが含まれていることに注意してください。ペイロードがこのフィールドの長さよりも長い場合は、拡張する必要があります。テーブルのデフォルトのアクションicmp_responderは、単にパケットをドロップします。

#### Step 1 - Connecting to P4Pi
ラップトップを「p4pi」と呼ばれるワイヤレスアクセスポイントに接続します。その後、ラップトップはDHCPサービスによって割り当てられたIPアドレスを取得します(デフォルトのアドレスプール192.168.4.0/24から)。

#### Step 2 - Launching the P4 program
SSHターミナルで次のコマンドを使用して手動で起動します。

```
echo 'arp_icmp' > /root/t4p4s-switch
systemctl restart t4p4s.service
```

#### Step 3 - Filling the tables

1. ssh経由でP4Piに接続します。

```
ssh pi@192.168.4.1
```

2. 次のヘルパースクリプトを使用してP4Runtimeシェルを起動します。

```
t4p4s-p4rtshell arp_icmp
```

3. ラップトップからのDHCP IPプールのプレフィックスと同じプレフィックスを持つ未使用のIPアドレスへのテストトラフィックの送信を開始します。たとえば、ラップトップでpingツールを使用できます。

```
[foo@localhost ~]$ ping -c 3 192.168.4.153
PING 192.168.4.153 (192.168.4.153) 56(84) bytes of data.
From 192.168.4.19 icmp_seq=1 Destination Host Unreachable
From 192.168.4.19 icmp_seq=2 Destination Host Unreachable
From 192.168.4.19 icmp_seq=3 Destination Host Unreachable

--- 192.168.4.153 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2030ms
pipe 3
```

pingは、ARP要求に対する応答がないため、ネットワーク内で宛先に到達できないことを報告します。
次に、pingの出力が「リクエストタイムアウト」に変わることがわかります。これは、宛先IPのMACアドレスが正常に解決されたが、pingによって送信されたICMPエコー要求に対する応答がないことを意味します。

4. P4Runtimeシェルでは、テーブルエントリarp_exactをテーブルに挿入できます。

```
te = table_entry["MyIngress.arp_exact"](action="MyIngress.arp_reply")
te.match["hdr.arp.dst_ip"] = "192.168.4.153"
te.action["request_mac"] = "aa:aa:aa:cc:cc:cc"
te.insert
```

次に、pingの出力が「リクエストタイムアウト」に変わることがわかります。これは、宛先IPのMACアドレスが正常に解決されたが、pingによって送信されたICMPエコー要求に対する応答がないことを意味します。

```
[foo@localhost ~]$ ping -c 3 192.168.4.153
PING 192.168.4.153 (192.168.4.153) 56(84) bytes of data.

--- 192.168.4.153 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2069ms
```

5. 次のエントリをテーブルに追加すると、返信icmp_responderがソースに返送されるため、ICMPエコー要求が処理されます。

```
te = table_entry["MyIngress.icmp_responder"](action="MyIngress.icmp_reply")
te.match["hdr.ethernet.dstAddr"] = "aa:aa:aa:cc:cc:cc"
te.match["hdr.ipv4.dstAddr"] = "192.168.4.152/31"
te.insert
```

このエントリを追加した後、pingは宛先に到達し、その可用性の報告を開始します。

```
[foo@localhost ~]$ ping -c 3 192.168.4.153
PING 192.168.4.153 (192.168.4.153) 56(84) bytes of data.
64 bytes from 192.168.4.153: icmp_seq=1 ttl=64 time=9.94 ms
64 bytes from 192.168.4.153: icmp_seq=2 ttl=64 time=9.34 ms
64 bytes from 192.168.4.153: icmp_seq=3 ttl=64 time=8.17 ms

--- 192.168.4.153 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 8.167/9.150/9.942/0.745 ms
```