SONiCでVRFを使う

# SONiCのVRFサポート
[SONiC](https://azure.github.io/SONiC/)はホワイトボックススイッチ用のOSです。ホワイトボックススイッチは、OSを別途インストールするタイプのネットワークスイッチ製品です。ネットワークスイッチ製品は、一般的にスイッチASICによるパケットのハードウェア転送機能を用いて高速にパケット処理を実行する機械です。

さて、VRF (Virtual Routing and Forwarding)はルーティングテーブルを複数保持することで、同一IPアドレスの通信であってもEthernetポートなどの条件と組み合わせて異なる宛先に転送することのできる機能です。世の中のスイッチASICのすべてがこの機能を持つわけではありませんが、ぼくの狭い見識で把握している限りでは、エンタープライズ向けと言われる多機能な製品においては標準的に搭載されている機能と言って差し支えないと思います。

もう何ヶ月か前になりますが、SONiCにVRFのサポートが加わりました。SONiCの設定としてVRFを定義し、インタフェースを所属させ、VRFごとに異なるルーティングテーブルを構築しパケット転送ができるということになります。

# SONiCのVRFの設定と参照
`/etc/sonic/config_db.json`にJSON形式でVRF設定を記述できます。

```
"VRF": {
    "Vrf1": {},
    "Vrf2": {}
}
"INTERFACE": {
    "Ethernet0": {
        "vrf_name": "Vrf1"
    },
    "Ethernet4": {
        "vrf_name": "Vrf2"
    },
    "Ethernet0|192.168.1.1/24": {},
    "Ethernet4|192.168.1.1/24": {}
```

設定のキモがいくつかあります。

- VRFの名称は `Vrf` から始まる必要がある。その後ろは任意の文字列でいい。
- インタフェースにVRFを指定するときは `|IPアドレス` とは別に設定しないといけない。

命名規則に従わないとVRFは作成されません。
`config`コマンドで設定することもできます。

```
sudo config vrf add Vrf1
sudo config vrf add Vrf2
sudo config interface vrf bind Ethernet0 Vrf1
sudo config interface vrf bind Ethernet4 Vrf2
sudo config interface ip add Ethernet0 192.168.1.1/24
sudo config interface ip add Ethernet4 192.168.1.1/24
```

`show vrf` で設定を参照できます。

```
$ show vrf
VRF    Interfaces
-----  ------------
Vrf2   Ethernet4
Vrf1   Ethernet0
```

# VRFの動作確認

それぞれのポートの接続先に`192.168.1.0/24`の範囲に収まるアドレスをつけて`ping`すれば応答があるか否かでかんたんな動作確認がとれますが、それだと本当にVRFが機能しているか判別できません。そこで、VRFを作成したスイッチ側から外のアドレスに`ping`を送ることを考えます。それぞれのポートの接続先のアドレスが異なっていれば、VRF指定の`ping`は異なるVRFのアドレスに届かないはずです。`ip vrf exec Vrf1 ping ...`などとすればいいはずですが、まずは表示がちゃんとできるか確認してみます。

```
$ ip vrf
Object "vrf" is unknown. try "ip help".
```

**あれ?**

# iproute2を新しくする
SONiCは現時点で、Debian 9.0 (stretch) をベースに構築されています。stretch向けのiproute2は、4.9.0であり`ip vrf`は未サポート。調べてみると、stretch-backportsから4.20.0を取得すればよさそうです。

SONiCはiproute2をどこでどうやって組み込んでいるのか。最初は`src/iproute2`なのかと思っていましたがどうやらそれは違っていて、`build_debian.sh`の中で`apt-get install`していることがわかりました。そこを変更してみます。

```iproute2.diff
diff --git a/build_debian.sh b/build_debian.sh
index 5b9a8e25..a46de51f 100755
--- a/build_debian.sh
+++ b/build_debian.sh
@@ -233,7 +233,6 @@ fi
 sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y i
nstall      \
     file                    \
     ifmetric                \
-    iproute2                \
     bridge-utils            \
     isc-dhcp-client         \
     sudo                    \
@@ -308,7 +307,8 @@ sudo LANG=C chroot $FILESYSTEM_ROOT bash -c "find /usr/share
/i18n/locales/ ! -na
 # Install certain fundamental packages from stretch-backports in order to get
 # more up-to-date (but potentially less stable) versions
 sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y -
t stretch-backports install \
-    picocom
+    picocom \
+    iproute2
 
 if [[ $CONFIGURED_ARCH == amd64 ]]; then
     sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y download \
```

# VRFの動作確認ふたたび

まず、iproute2が意図通り更新されているかを確認。

```
$ ip vrf
Name              Table
-----------------------
Vrf1              1001
Vrf2              1002
```

無事動作するようになりました。では、いざ`ping`を!

```
$ sudo ip vrf exec Vrf1 ping 192.168.1.2
Failed to load BPF prog: 'Invalid argument'
Kernel compiled with CGORUP_BPF enabled?
```

**ほえ?**

# CGROUP_BPF

SONiCで使用されるLinux kernelは、ソースからビルドする場合`src/sonic-linux-kernel`の下に展開されます。そこで検索してみたところ、`CGROUP_BPF`という文字列はひとかけらも含まれていませんでした。

情報を求めてWebの海をさまようと、情報が見つかりました。
https://cateee.net/lkddb/web-lkddb/CGROUP_BPF.html
このページによると、

> found in Linux kernels: 4.10–4.20, 5.0–5.5, 5.6-rc+HEAD

とのこと。SONiCで使われているカーネルは4.9.189-3+deb9u2です。つまり、**未サポート**。
カーネルバージョンを差し替えるといろんなものが確認やりなおしになるため、いったんあきらめることにします。無念。

# 結論

SONiCでVRFは使えます。そして、`ip vrf`をSONiC上で動かせるようになりました。
しかし残念ながら、現時点のSONiCでは **`ip vrf exec`は使えません**。
VRFが動いているかの確認方法は別途考えることにします。

おしまい。

#### 追伸
[stretch-backportsにkernel 4.19がある](https://packages.debian.org/stretch-backports/kernel/)ので、適用してみるのも一つの手かと考えています。
これは後日試す予定。→ 「[SONiCでip vrfを使いたい](https://qiita.com/masru0714/items/aaee2b4f969b4fab381d)」