1. はじめに
前回の記事で、NW機器のリンク・筐体障害発生と断時間計測を自動化する例をご紹介しました。
pyATS/GenieでNW障害テストを自動化する
ラボ環境では何度でも自由に障害テスト出来ますが、本番環境リリース後はそうは行かないと思います。
また、新規セグメント構築やルーティング・ACL設定追加を積み重ねていく中で設定ミスが生じ、障害発生時の系切替やサイト切替で問題が発覚するケースもあると思います。
今回ご紹介するOSSのネットワークConfig解析ツール「Batfish」では、実機のConfigファイルをインプットとして、インターフェース、ルーティング(BGP、OSPF、EIGRP、Static等)、ACL設定等の解析を行い、ある機器間が通信可能かシミュレート出来ます。
さらに、Batfish上で疑似的にリンク・筐体障害を発生させ、障害時の疎通性や通信経路も確認可能です。
これ以降で、実際に障害時の影響分析を行う例をメモしておきます。
2. ネットワーク構成
詳細は割愛しますが、CML上に以下の環境を構築しました。
-
csr1000v-0
⇒iosvl2-0,1
向け
csr1000v-0
で、10.0.0.0/8
向けのStaticルーティングを設定。
ネクストホップはVRRP VIPとし、通常時はMasterのiosvl2-0
を経由。 -
iosvl2-0,1
~iosvl2-3,4
間
各VLAN IFで、シングルエリア構成でOSPFを有効化し、等コストマルチパスで冗長化。
3. Batfishのセットアップ
3-1. Batfish Dockerイメージの取得、コンテナ起動
DockerエンジンをインストールしたLinux等を用意し、docker pull
コマンドでBatfishのイメージを取得します。
Batfishサービス本体のみのbatfish/batfishと、クライアント環境(pybatfish、Jupyter Notebook)を含んだbatfish/allinoneがありますが、今回は前者を使用しました。
バージョンは2022年3月時点の最新版2021.12.13.1143
にしています。
続いて、docker run
コマンドでコンテナを起動します。
$ sudo docker pull batfish/batfish:2021.12.13.1143
$ sudo docker run -d --name batfish -p 9997:9997 -p 9996:9996 batfish/batfish:2021.12.13.1143
3-2. pybatfishのインストール
BatfishのPythonベースのクライアントpybatfish
を上記のコンテナホストもしくは別の端末にインストールします。
今回は、別の端末のPython仮想環境上(venv)にインストールしました。
(venv) $ python3 -m pip install --upgrade pip
(venv) $ python3 -m pip install --upgrade pybatfish
3-3. pybatfishの初期セットアップ
- Configの保管
クライアント側に以下のようなディレクトリを作成し、解析対象のConfig(iosvl2、nxos9000)を保管しておきます。
Config保管ディレクトリ名/
┗━ configs/
┣━ router1.cfg
┣━ router2.cfg
┗━ ...
-
コネクションのセットアップ
バージョン2021.11.04.1095
リリースの際、pybatfishとBatfishサービス間のコネクションセットアップはSession()
を使うようドキュメント改訂されています。
pybatfish > Interacting with the Batfish service > Sessions以前はBatfishサービスをリモートで起動する場合、コードの修正が必要でしたが、
Session()
の引数host
でIPアドレスを指定可能になりました(デフォルトはlocalhost
です)。
また、batfish/batfish
コンテナは通常TCP/9997,9996
を使用しますが、何らかの事情で別のポート番号を使用したり、複数のBatfishコンテナを同時起動したい場合は引数port_v1
、port_v2
で変更可能です。
import pandas as pd
from pybatfish.client.session import Session
from pybatfish.datamodel import *
from pybatfish.datamodel.answer import *
from pybatfish.datamodel.flow import *
pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)
pd.set_option("display.max_colwidth", None)
# bf = Session(host="localhost")
bf = Session(host="<Batfishサービスのアドレス>", port_v1=9997, port_v2=9996)
- ネットワーク、スナップショットの作成
ネットワークとは、各機器やサブネット情報を論理的にグルーピングしたものです。解析対象ネットワークが1種類の場合は、最初に1つ作成すれば問題ないと思います。今回はtest_network
を作成しています。
またスナップショットとは、Configファイルから解析したネットワーク状態をファイル群としてまとめたものです。今回はまず、正常時のConfigファイルからスナップショットtest_snapshot
を作成しています。
ディレクトリは上記のConfig保管ディレクトリを指定します。
NETWORK_NAME = "test_network"
BASE_SNAPSHOT_NAME = "test_snapshot"
SNAPSHOT_DIR = '<Config保管ディレクトリ>'
# Create a network
bf.set_network(NETWORK_NAME)
# Initializing a new snapshot
bf.init_snapshot(SNAPSHOT_DIR, name=BASE_SNAPSHOT_NAME, overwrite=True)
- 認識出来ないコマンドの確認
Batfishは様々なベンダーのConfigをサポートしていますが、認識出来ないコマンドもあります。
解析結果が想定と異なる場合、重要な手掛かりになりますので、ここで該当のコマンド一覧を取得しておきます。
bf.q.initIssues().answer()
Jupyter Notebookで実行した結果は以下の通りです。icam monitor scale
やcopp profile strict
が認識できていないようです。
4. 正常時の確認
4-1. VRRP確認
まずは正常時のVRRPステータスを確認してみます。pybatfishでQuestionを実行することで、Batfishサービスへ各種問合せが可能です。VRRPの場合vrrpProperties()
を使用します。
bf.q.vrrpProperties().answer().frame()
想定通りConfig解析が行われ、iosvl2-0
がMaster、iosvl2-1
がBackupとなっている事が分かります。
(参考) iosvl2-0
のVlan100設定
interface Vlan100
ip address 10.1.1.253 255.255.255.0
ip access-group acl_test in
vrrp 100 ip 10.1.1.254
vrrp 100 priority 110
4-2. ルーティング確認
ルーティングではroutes()
を使用します。引数を指定しない場合、全機器の情報が表示されます。以下の例はnodes="csr1000v-0"
で1台に絞っています。
bf.q.routes(nodes="csr1000v-0").answer().frame()
想定通りStatic、直接接続、ローカルのルーティング情報が表示されています。
別の例として、全ルーティング情報を取得した後「宛先が10.4.1.0/24
かつプロトコルがospf
」のルートのみフィルタすることも可能です。
routes_df = bf.q.routes().answer().frame()
routes_df[(routes_df['Network'] == "10.4.1.0/24") & (routes_df["Protocol"] == "ospf")]
4-3. OSPF確認
OSPFのネイバー関係はospfSessionCompatibility()
で確認可能です。こちらもデフォルトでは全機器の情報が表示されますので、.head(5)
で最初の5つだけ表示しています。
bf.q.ospfSessionCompatibility().answer().frame().head(5)
4-4. ACL設定確認
上記のようなステータス確認ではなく、Config設定も確認できます。例えばACL設定の場合、definedStructures()
でtypes="extended ipv4 access-list line"
を指定すれば「IPv4拡張ACL」を表示可能です。
bf.q.definedStructures(types="extended ipv4 access-list line").answer().frame()
4-5. 通信経路確認
最後に、ネットワーク構成図のPing送信元10.1.1.100
から10.4.1.100
宛ての疎通性および通信経路を確認してみます。
下記参考の通りいくつか選択肢がありますが、今回はbidirectionalTraceroute()
を使ってみます。
引数としてstartLocation='@enter(<開始ノード名>[<IF名>])'
、headers=HeaderConstraints(dstIps='<宛先アドレス>', srcIps='<送信元アドレス>'))
を指定します。
bidirectionalと名が付いている通り、双方向の結果を返してくれます。以下の例では、Forward_Traces[0][0]
で行きの経路の1つを表示しています。
result_bdtr = bf.q.bidirectionalTraceroute(
startLocation='@enter(csr1000v-0[GigabitEthernet2])',
headers=HeaderConstraints(dstIps='10.4.1.100', srcIps='10.1.1.100')).answer().frame()
result_bdtr.Forward_Traces[0][0]
1.csr1000v-0
⇒2.iosvl2-0
(VRRP Master)⇒3.nxos9000-0
⇒4.iosvl2-2
の経路で通信できている事が分かります。また、RECEIVED
、FORWARDED
、TRANSMITTED
でそれぞれ受信、フォワーディング、送信の流れも確認できます。2.でPERMITTED
と記載されている通り、ACL許可されている事も分かります。
また以下の例では、Reverse_Traces[0][0]
で戻りの経路の1つを表示しています。
result_bdtr.Reverse_Traces[0][0]
行きの場合、宛先の10.4.1.100
を持つ機器が存在しないため結果がDELIVERED_TO_SUBNET
となっていますが、戻りの場合csr1000v-0
が10.1.1.100
を持っているためACCEPTED
となっています。
(参考) 通信経路確認で用いるQuestion
traceroute
とreachablity
の違いがややこしいのですが、前者は「Tracerouteのように、ある特定の送信元・宛先ホストの組み合わせが許可/拒否されるか確認する」用途なのに対し、後者は「ある送信元・宛先セグメントの組み合わせがすべて許可されるか、あるいは1つでも拒否される組み合わせがあるか検索する」用途のイメージです。
No. | Question | 内容 |
---|---|---|
1 | traceroute (※) | 指定した開始ノードから宛先へ仮想的なTracerouteを行う。実際のTracerouteと異なり、各ホップの戻り通信がNGでもIPアドレスは返してくれる |
2 | bidirectionalTraceroute (※) | No.1に加え、戻りの確認も同時に行う |
3 | reachability (※) | 指定した経路(開始/終了/経由箇所)、送信元/宛先情報(プロトコル、アドレス、ポート番号等)にマッチするフローを検索し、フローの例を出力 |
4 | bidirectionalReachability | No.3に加え、戻りの確認も同時に行う |
5 | differentialReachability | ある構成(現状)では許可されるが、別の構成(設定後)ではドロップされるフローを検出 |
(※) 引数ignoreFilters=True
を指定することでACL設定を無視した経路確認が可能。
5. 最後に
ここまでで、Batfishを使って正常時の確認を行いました。別の記事で障害時の確認と、正常時・障害時を比較した影響分析も行ってみたいと思います。