マルチレイヤなNWトポロジ情報から検証環境を作ってみる(1) - 概要

## はじめに

これまで

- [Batfish を使ってネットワーク構成を可視化してみよう (1) - Qiita](https://qiita.com/corestate55/items/8a39af553785fd77c20a)
- [Batfish を使ってネットワーク構成を可視化してみよう・改 - Qiita](https://qiita.com/corestate55/items/fb18066d1105010758d9)

……など、NW 機器の設定ファイルをもとに NW の構成情報を取り出し、可視化する話をしてきました。ちょっと間が空いてしまいましたが、今回はその「逆変換」的な処理……構成情報を元に機器の設定を起こして、検証用の NW を作る話をします。ターゲットとしては [Batfish を使ってネットワーク構成を可視化してみよう・改](https://qiita.com/corestate55/items/fb18066d1105010758d9) であつかった L3/OSPF/BGP のネットワークを使います。

さて。「逆変換」ですが、これができると何がうれしいのでしょうか? そもそも何をやりたいのかをまとめてみたのが下の図です。これまでは右側(実環境)→左側(人)への方向をやってきていました。今回やりたいのは左側→右側のところですね。システムの構成情報ありき (人が作る・実環境からとってくる) の状態で、模擬環境を起こす・動作確認をする・問題がなければ実環境で作業をするといったプロセスを意識した方向です。

![Goal image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154230/fb256a8b-b8e1-c6ec-e98f-c00f18161900.png)

もしそういうことができたとすると?

- システムの変更は「図 (トポロジ情報) を操作する」 ことでおこなう
- 実環境投入前に模擬環境でテストをする
    - 模擬環境は短時間で生成できる、手動/自動のテストを入れられる、テスト手順やテストスクリプト等を開発する場として使える
        - 問題があれば見直して「図を描きなおす」
        - 問題がなければ設定情報を実環境に投入する (理想的には模擬環境と本番環境で差が出ないことですが…、実際には模擬環境と実環境で完全にイコールにはならないでしょう)
    - 模擬環境でうまくいったら本番環境に投入する (push-on-green)
- 実環境が実際にどう動くのかを同じ構成情報で取得できる
    - 実際のステートなどをその上にマッピングして可視化できる
    - 模擬環境・時間スナップショットをとって現在との差分を機械的に表示できる
    - 実状態や変化内容を基にとるべき対応を(人が)決定する
        - 「図」を変更して模擬環境テスト→実環境操作のプロセスへ

こういうぐるぐる回るようなサイクルが作れないかな、というのがやりたいことです。今回の実験では以下のようなツールセットになります。前回の記事では下半分側、今回の記事では上半分側(ベージュ背景部分)をやっています。いきなり上にあげた要素を全部フルセットではやれないので、限定したユースケースでいったんぐるっと回せるかどうかの実験です。

![Toolset](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154230/775e77ec-cc29-14fd-d342-bc9482ff9f89.png)

## トポロジ情報から検証環境をつくる

### 対象ネットワーク

[ひきつづき下図のネットワークを対象とします](https://qiita.com/corestate55/items/fb18066d1105010758d9)。つくりとして L3 = L1 になっています。L2 周りの話を入れるとややこしくなるので、まずはモデル化しやすい、シンプルな L3 構成のネットワークから。

- Multiple area OSPF (AS65531 IGP)
- BGP
    - Confederation (AS65531-AS65532)
    - Route reflector (core01-border11, core02-border12)

![Target network](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154230/e2eb7027-1321-b630-7cf4-567c5b8aa784.png)

### 何を使って作るか?

今回、検証環境をつくるにあたって [tinynetwork/tinet: TiNET](https://github.com/tinynetwork/tinet) を使っています。これがどういったツールなのかについては、OSC のセミナでわかりやすく解説されている動画があるのでそちらを参照してください。

- [Dockerで始めるネットワーク実験入門 2020-5-30 C-4 - YouTube](https://www.youtube.com/watch?v=_gaeI56vmPI&feature=youtu.be&ab_channel=OSPN.jp)

計算機上でのネットワークエミュレーションについては、OSC セミナ動画の中でもいくつか類似のものが紹介されています。なぜ mininet などの他のツールではなく tinet にしたのかというとこんな感じです :

- tinet はネットワーク周りを外した docker container に対して veth 等での足回り機能を提供する
    - Container based (FRRなどの一般的なルーティングデーモンが使える) → 元のネットワーク(本番環境相当) が Cisco router なので Cisco like なコンフィグで模擬環境が作れる。(mininet の場合、netns 内で動かすプロセスのセットアップからやらないといけなくて面倒…)
    - tinet は veth 操作のための shell script を出力する → 何がおきてどういう操作をしているのかのチェックがしやすい
    - シンプルで軽い
- サンプルがたくさんある
    - [https://github.com/tinynetwork/tinet/tree/master/examples](https://github.com/tinynetwork/tinet/tree/master/examples)

GNS3 や CML を使う手もあったかもしれないけど、インストールの手間とかルータ間接続(トポロジ)操作の自動化とかを考えると、今回の用途にはちょっと難があるかなと。tinet がシンプルで割とさくっとお試しできそうなのでそっちにしました。mininet も調べてると docker container に対応したもの ([Containernet](https://containernet.github.io/)) があるみたいなので、これはそのうち調べてみてもいいかもしれない。あるいは、ベンダが提供しているコンテナベースのルーティング製品が使える [containerlab](https://containerlab.srlinux.dev/) も面白そうです。

## できたもの

細かい話をする前に、何がどこまでできているのかをざっと見ていきます。[実環境 (という設定の GNS3 で組んだ実験用ネットワーク) の router config からトポロジデータ (RFC8345) を出力して、L3/OSPF/BGP の情報を可視化するところが出発点](https://qiita.com/corestate55/items/fb18066d1105010758d9)です。今回紹介するのはこのへん :

- トポロジデータ から本番模擬(検証)環境をつくる
    - L3 : ルータ間接続 (いま L3=L1 なので)  とインタフェースのIPアドレス設定
    - OSPF : OSPF コンフィグの設定
    - BGP : BGP コンフィグの設定
- 模擬環境で経路情報の確認・通信試験ができる
    - 一部通信試験はトポロジデータから自動生成・自動実行する

以下どんなものができたのか説明していきますが、正直もうちょっと動かしてる様子とか見えないとわからん感がすごい。なので簡単な動画作ってみました。本当に動かしてるだけですが…。一応 元環境の Config が手元にある状態で、Batfish による解析→トポロジファイル作成→Tinet/FRR コンフィグへの変換→環境起動→テスト実行、という流れです。このあたりまで含めて 5 分くらいあれば終わる感じですね。[^timer]

[^timer]: BGP のタイマー設定がデフォルトなので、経路情報が収束するのにちょっと時間がかかる状態になっています。

<iframe width="560" height="315" src="https://www.youtube.com/embed/GGRmhJn9ba4" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

### トポロジから模擬環境の定義ファイルを生成する

使うスクリプト類は [netomox-examples/config_defs](https://github.com/corestate55/netomox-examples/tree/develop/config_defs) にあります。

操作手順だけ見るとそんなに大層なことないです。まず、[トポロジファイル](https://github.com/corestate55/netoviz/blob/develop/static/model/bf_l3s1.json)から [tinet 用の spec.yaml](https://github.com/corestate55/netomox-examples/blob/develop/config_defs/spec.yaml) を生成します。spec.yaml には、ルータとして使用するイメージ名の定義やルータ間接続情報 (トポロジ情報)、ルータ設定のためのコマンドなどが含まれています。

```
hagiwara@dev02:~/nwmodel/netomox-examples$ bundle exec ruby config_defs/topo2config.rb > config_defs/spec.yaml
```

あとは tinet で起動していくだけ。

```
hagiwara@dev02:~/nwmodel/netomox-examples/config_defs$ tinet upconf -c spec.yaml | sudo sh -x
[...]
hagiwara@dev02:~/nwmodel/netomox-examples/config_defs$ docker container ls
CONTAINER ID   IMAGE          COMMAND       CREATED          STATUS          PORTS     NAMES
6f764521533c   slankdev/frr   "/bin/bash"   28 seconds ago   Up 27 seconds             core02
f537d7bc9d5e   slankdev/frr   "/bin/bash"   28 seconds ago   Up 27 seconds             core01
ed79c99ed5c2   slankdev/frr   "/bin/bash"   28 seconds ago   Up 27 seconds             border21
f43f931f8849   slankdev/frr   "/bin/bash"   29 seconds ago   Up 28 seconds             border12
b6749c338eae   slankdev/frr   "/bin/bash"   29 seconds ago   Up 28 seconds             border11
c74b4c953fcf   slankdev/frr   "/bin/bash"   29 seconds ago   Up 28 seconds             border01
8a183c6e9003   slankdev/frr   "/bin/bash"   29 seconds ago   Up 28 seconds             as65534_10.0.1.13
34077ce33a00   slankdev/frr   "/bin/bash"   30 seconds ago   Up 29 seconds             as65534_10.0.0.45
f1264096660c   slankdev/frr   "/bin/bash"   30 seconds ago   Up 29 seconds             as65533_10.0.0.17
hagiwara@dev02:~/nwmodel/netomox-examples/config_defs$
```

これで spec.yaml の内容に沿ってルータ (コンテナ) が起動しているので、あとはコンテナの中に入って操作していきます。

```
hagiwara@dev02:~/nwmodel/netomox-examples/config_defs$ docker container exec -it border11 vtysh -c "sh version"
FRRouting 6.0 (border11).
Copyright 1996-2005 Kunihiro Ishiguro, et al.
[...]
hagiwara@dev02:~/nwmodel/netomox-examples/config_defs$
```

tinet config には `test` セクションがあります。各コンテナ等を起動した後にチェックのためのコマンドをこのセクションに書いておいて、まとめて実行することができます。テストを実行すると、spec.yaml 内に定義したテスト用コマンド: 隣接インタフェース間での疎通テストなどをまとめて実行できます。いまは各ルータの状態確認 + 通信確認 (ping) コマンド群をトポロジデータから生成しています:　この辺の工夫点とかは追々。)

```
hagiwara@dev02:~/nwmodel/netomox-examples/config_defs$ tinet test -c spec.yaml | sudo sh -x
+ docker exec as65533_10.0.0.17 vtysh -c show interface
[...]
+ docker exec as65533_10.0.0.17 vtysh -c show running-config
[...]
+ docker exec as65533_10.0.0.17 ping -c2 10.0.0.18
PING 10.0.0.18 (10.0.0.18) 56(84) bytes of data.
64 bytes from 10.0.0.18: icmp_seq=1 ttl=64 time=0.035 ms
64 bytes from 10.0.0.18: icmp_seq=2 ttl=64 time=0.083 ms

--- 10.0.0.18 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1013ms
rtt min/avg/max/mdev = 0.035/0.059/0.083/0.024 ms
+ docker exec as65534_10.0.0.45 ping -c2 10.0.0.46
PING 10.0.0.46 (10.0.0.46) 56(84) bytes of data.
64 bytes from 10.0.0.46: icmp_seq=1 ttl=64 time=0.032 ms
64 bytes from 10.0.0.46: icmp_seq=2 ttl=64 time=0.029 ms

--- 10.0.0.46 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1005ms
rtt min/avg/max/mdev = 0.029/0.030/0.032/0.005 ms
[...]
```

ちなみに、 `tinet img` で tinet が構成したトポロジを graphviz DOT 形式で出力できたりもしますね。(参照: [Graphviz Online](https://dreampuf.github.io/GraphvizOnline/#digraph%20%20%7B%0A%0A%20%20%20%20%20%20%20%20n1%5Blabel%3D%22as65533_10.0.0.17%22%5D%3B%0A%20%20%20%20%20%20%20%20n3%5Blabel%3D%22as65534_10.0.0.45%22%5D%3B%0A%20%20%20%20%20%20%20%20n5%5Blabel%3D%22as65534_10.0.1.13%22%5D%3B%0A%20%20%20%20%20%20%20%20n2%5Blabel%3D%22border01%22%5D%3B%0A%20%20%20%20%20%20%20%20n9%5Blabel%3D%22border11%22%5D%3B%0A%20%20%20%20%20%20%20%20n4%5Blabel%3D%22border12%22%5D%3B%0A%20%20%20%20%20%20%20%20n6%5Blabel%3D%22border21%22%5D%3B%0A%20%20%20%20%20%20%20%20n7%5Blabel%3D%22core01%22%5D%3B%0A%20%20%20%20%20%20%20%20n8%5Blabel%3D%22core02%22%5D%3B%0A%20%20%20%20%20%20%20%20n1-%3En2%5Barrowhead%3D%22none%22%2Cfontsize%3D%228%22%2Cheadlabel%3D%22Fa0-0%22%2Clabelfloat%3D%22true%22%2Ctaillabel%3D%2210.0.0.17%22%5D%3B%0A%20%20%20%20%20%20%20%20n3-%3En4%5Barrowhead%3D%22none%22%2Cfontsize%3D%228%22%2Cheadlabel%3D%22Fa1-1%22%2Clabelfloat%3D%22true%22%2Ctaillabel%3D%2210.0.0.45%22%5D%3B%0A%20%20%20%20%20%20%20%20n5-%3En6%5Barrowhead%3D%22none%22%2Cfontsize%3D%228%22%2Cheadlabel%3D%22Fa1-0%22%2Clabelfloat%3D%22true%22%2Ctaillabel%3D%2210.0.1.13%22%5D%3B%0A%20%20%20%20%20%20%20%20n2-%3En7%5Barrowhead%3D%22none%22%2Cfontsize%3D%228%22%2Cheadlabel%3D%22Fa1-0%22%2Clabelfloat%3D%22true%22%2Ctaillabel%3D%22Fa1-0%22%5D%3B%0A%20%20%20%20%20%20%20%20n2-%3En8%5Barrowhead%3D%22none%22%2Cfontsize%3D%228%22%2Cheadlabel%3D%22Fa1-0%22%2Clabelfloat%3D%22true%22%2Ctaillabel%3D%22Fa1-1%22%5D%3B%0A%20%20%20%20%20%20%20%20n9-%3En4%5Barrowhead%3D%22none%22%2Cfontsize%3D%228%22%2Cheadlabel%3D%22Fa0-0%22%2Clabelfloat%3D%22true%22%2Ctaillabel%3D%22Fa0-0%22%5D%3B%0A%20%20%20%20%20%20%20%20n9-%3En7%5Barrowhead%3D%22none%22%2Cfontsize%3D%228%22%2Cheadlabel%3D%22Fa1-1%22%2Clabelfloat%3D%22true%22%2Ctaillabel%3D%22Fa1-0%22%5D%3B%0A%20%20%20%20%20%20%20%20n9-%3En6%5Barrowhead%3D%22none%22%2Cfontsize%3D%228%22%2Cheadlabel%3D%22Fa1-1%22%2Clabelfloat%3D%22true%22%2Ctaillabel%3D%22Fa1-1%22%5D%3B%0A%20%20%20%20%20%20%20%20n4-%3En8%5Barrowhead%3D%22none%22%2Cfontsize%3D%228%22%2Cheadlabel%3D%22Fa1-1%22%2Clabelfloat%3D%22true%22%2Ctaillabel%3D%22Fa1-0%22%5D%3B%0A%20%20%20%20%20%20%20%20n7-%3En8%5Barrowhead%3D%22none%22%2Cfontsize%3D%228%22%2Cheadlabel%3D%22Fa0-0%22%2Clabelfloat%3D%22true%22%2Ctaillabel%3D%22Fa0-0%22%5D%3B%0A%0A%7D%0A))

![1_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154230/dc4c6d01-7b98-9730-25f2-b4426dcd0dde.png)

### 模擬環境の経路情報を元の環境の情報と比較してみる

tinet で作った模擬環境は、元になった実環境と L3 構成・その経路制御の観点では同等になっています[^exc]。 ためしに border11 の経路情報を見てみましょう。まとめて出すとわかりにくいので、OSPF, BGP で分けて出しています: 対象環境は BGP Confederation, Route Reflector が含まれる環境であることに注意してください。border11 は Confederation border & RR Client です。

[^exc]: なってるはずです。そうなるようにトポロジ情報から構成情報とか経路制御設定とかを出力しているので、そうなってないとなにかミスしている…。ただ、外部の AS (AS65533, AS65534) については内部 (AS65530) の eBGP 情報を元に外挿しているのでそのあたりは元環境と違いが出ます。

元環境の border11 (Cisco IOS on GNS3)

```
border11#sh ip route ospf
     10.0.0.0/8 is variably subnetted, 19 subnets, 4 masks
O E2    10.0.0.2/32 [110/20] via 10.0.0.33, 06:16:25, FastEthernet1/0
O E2    10.0.0.3/32 [110/20] via 10.0.0.42, 06:16:15, FastEthernet0/0
O E2    10.0.0.1/32 [110/20] via 10.0.0.33, 06:15:55, FastEthernet1/0
O E2    10.0.0.5/32 [110/20] via 10.0.0.42, 06:16:15, FastEthernet0/0
O IA    10.0.0.24/30 [110/3] via 10.0.0.42, 06:16:15, FastEthernet0/0
                     [110/3] via 10.0.0.33, 06:16:25, FastEthernet1/0
O IA    10.0.0.28/30 [110/2] via 10.0.0.33, 06:16:25, FastEthernet1/0
O E2    10.0.0.16/30 [110/20] via 10.0.0.33, 06:15:55, FastEthernet1/0
O IA    10.0.0.20/30 [110/2] via 10.0.0.33, 06:16:25, FastEthernet1/0
O E2    10.0.0.44/30 [110/20] via 10.0.0.42, 06:16:15, FastEthernet0/0
O       10.0.0.36/30 [110/2] via 10.0.0.42, 06:16:15, FastEthernet0/0
border11
border11#sh ip route bgp
     10.0.0.0/8 is variably subnetted, 19 subnets, 4 masks
B       10.2.0.0/16 [200/0] via 10.0.1.13, 06:15:11
B       10.0.0.0/16 [200/0] via 10.0.0.1, 06:15:17
B       10.1.0.0/16 [200/0] via 10.0.0.17, 06:15:17
B       10.0.1.0/24 [200/0] via 10.0.1.10, 06:15:59
border11#
border11#sh ip bgp all
For address family: IPv4 Unicast
BGP table version is 7, local router ID is 10.0.0.4
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,
              r RIB-failure, S Stale
Origin codes: i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*> 10.0.0.0/24      0.0.0.0                  0         32768 i
*>i10.0.0.0/16      10.0.0.1                 0    100      0 i
*> 10.0.1.0/24      10.0.1.10                0    100      0 (65532) i
*>i10.1.0.0/16      10.0.0.17                0    100      0 65533 i
*> 10.2.0.0/16      10.0.1.13                0    100      0 (65532) 65534 i
border11#
border11#sh ip cef 10.0.0.0/16 detail
10.0.0.0/16, epoch 0
  recursive via 10.0.0.1
    nexthop 10.0.0.33 FastEthernet1/0
border11#
```

模擬環境の border11 (FRR container with tinet)

- `ip route ospf` に directly connected の経路も含まれています (IOS では含まれない)
- `ip route bgp` では recursive lookup された情報が表示されます (IOS はそこまで表示していない: `ip cef detail` の表示と対比してみてください)

```
border11# sh ip route ospf
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR,
       > - selected route, * - FIB route

O>* 10.0.0.1/32 [110/20] via 10.0.0.33, Fa1-0, 00:14:51
O>* 10.0.0.2/32 [110/20] via 10.0.0.33, Fa1-0, 00:14:51
O>* 10.0.0.3/32 [110/20] via 10.0.0.42, Fa0-0, 00:14:44
O>* 10.0.0.5/32 [110/20] via 10.0.0.42, Fa0-0, 00:14:59
O>* 10.0.0.16/30 [110/20] via 10.0.0.33, Fa1-0, 00:14:51
O>* 10.0.0.20/30 [110/20] via 10.0.0.33, Fa1-0, 00:14:52
O>* 10.0.0.24/30 [110/30] via 10.0.0.33, Fa1-0, 00:14:45
  *                       via 10.0.0.42, Fa0-0, 00:14:45
O>* 10.0.0.28/30 [110/20] via 10.0.0.33, Fa1-0, 00:14:52
O   10.0.0.32/30 [110/10] is directly connected, Fa1-0, 00:15:47
O>* 10.0.0.36/30 [110/20] via 10.0.0.42, Fa0-0, 00:15:00
O   10.0.0.40/30 [110/10] is directly connected, Fa0-0, 00:15:05
O>* 10.0.0.44/30 [110/20] via 10.0.0.42, Fa0-0, 00:14:59
border11# 
border11# sh ip route bgp
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR,
       > - selected route, * - FIB route

B>  10.0.0.0/16 [200/0] via 10.0.0.1 (recursive), 00:15:06
  *                       via 10.0.0.33, Fa1-0, 00:15:06
B>* 10.0.1.0/24 [200/0] via 10.0.1.10, Fa1-1, 00:16:01
B>  10.1.0.0/16 [200/0] via 10.0.0.17 (recursive), 00:15:06
  *                       via 10.0.0.33, Fa1-0, 00:15:06
B>  10.2.0.0/16 [200/0] via 10.0.1.13 (recursive), 00:16:01
  *                       via 10.0.1.10, Fa1-1, 00:16:01
border11# sh bgp detail 
BGP table version is 5, local router ID is 10.0.0.4, vrf id 0
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*>i10.0.0.0/16      10.0.0.1                 0    100      0 i
*> 10.0.0.0/24      0.0.0.0                  0         32768 i
*> 10.0.1.0/24      10.0.1.10                0    100      0 (65532) i
*>i10.1.0.0/16      10.0.0.17                0    100      0 65533 i
*> 10.2.0.0/16      10.0.1.13                0    100      0 (65532) 65534 i

Displayed  5 routes and 5 total paths
border11#
```

## まとめ

実環境 = Ciscoベースの L3 Network に対して、実環境の機器コンフィグから経路制御設定を含む複数レイヤのトポロジ情報を生成し、そこからさらに模擬環境 (検証環境) を構築できました。この模擬環境は実環境と Layer3 的には同等の機能 (OSPF/BGP による経路制御まで同等) を持っています。したがって、本番環境での経路制御設定を変更するオペレーションなどを考える際には、一度現時点の実環境コンフィグを元に検証環境を生成し、その中でコマンドの確認・狙った経路制御設定として動作するかどうかを実際に動かして動作確認 (オペレーションの検討・検証・練習 etc) ができるようになっています。

まあこれだけだと、本番環境に近い検証環境をお手軽に作れて、そこで検証とかができたとしても、変更とかがあったらまた本番用のなにかに置き換えないといけないわけです。その点、"はじめに" でやりたいことにあげた「サイクルを作る」はまだです。今後どういうことができると良さそうかは続く記事の中で出していこうと思います。

そんなわけで続きます。まず大枠、やりたいこととどんなものができたのかをお見せしたので、次は中身の話をしましょう。[次回は、トポロジ情報に含まれる各レイヤ (L3, OSPF, BGP) をそれぞれどう変換しているか](https://qiita.com/corestate55/items/02f6ce5c6b0af47220dc)について説明します。