2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

BatfishでNW障害時の影響分析を行う①

Posted at

1. はじめに

前回の記事で、NW機器のリンク・筐体障害発生と断時間計測を自動化する例をご紹介しました。
pyATS/GenieでNW障害テストを自動化する

ラボ環境では何度でも自由に障害テスト出来ますが、本番環境リリース後はそうは行かないと思います。
また、新規セグメント構築やルーティング・ACL設定追加を積み重ねていく中で設定ミスが生じ、障害発生時の系切替やサイト切替で問題が発覚するケースもあると思います。

今回ご紹介するOSSのネットワークConfig解析ツール「Batfish」では、実機のConfigファイルをインプットとして、インターフェース、ルーティング(BGP、OSPF、EIGRP、Static等)、ACL設定等の解析を行い、ある機器間が通信可能かシミュレート出来ます。
さらに、Batfish上で疑似的にリンク・筐体障害を発生させ、障害時の疎通性や通信経路も確認可能です。

これ以降で、実際に障害時の影響分析を行う例をメモしておきます。

2. ネットワーク構成

詳細は割愛しますが、CML上に以下の環境を構築しました。

  • csr1000v-0iosvl2-0,1向け
    csr1000v-0で、10.0.0.0/8向けのStaticルーティングを設定。
    ネクストホップはVRRP VIPとし、通常時はMasterのiosvl2-0を経由。

  • iosvl2-0,1iosvl2-3,4
    各VLAN IFで、シングルエリア構成でOSPFを有効化し、等コストマルチパスで冗長化。

  • alpine-0iosvl2-3,4向け
    HSRPを使用し、通常時はActiveのiosvl2-2を経由。
    image.png

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_v1port_v2で変更可能です。

snippet1
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保管ディレクトリを指定します。
snippet2
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をサポートしていますが、認識出来ないコマンドもあります。
    解析結果が想定と異なる場合、重要な手掛かりになりますので、ここで該当のコマンド一覧を取得しておきます。
snippet3
bf.q.initIssues().answer()

  Jupyter Notebookで実行した結果は以下の通りです。icam monitor scalecopp profile strictが認識できていないようです。
image.png

4. 正常時の確認

4-1. VRRP確認

まずは正常時のVRRPステータスを確認してみます。pybatfishでQuestionを実行することで、Batfishサービスへ各種問合せが可能です。VRRPの場合vrrpProperties()を使用します。

snippet4
bf.q.vrrpProperties().answer().frame()

想定通りConfig解析が行われ、iosvl2-0がMaster、iosvl2-1がBackupとなっている事が分かります。
image.png
(参考) 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台に絞っています。

snippet5
bf.q.routes(nodes="csr1000v-0").answer().frame()

想定通りStatic、直接接続、ローカルのルーティング情報が表示されています。
image.png

別の例として、全ルーティング情報を取得した後「宛先が10.4.1.0/24かつプロトコルがospf」のルートのみフィルタすることも可能です。

snippet6
routes_df = bf.q.routes().answer().frame()
routes_df[(routes_df['Network'] == "10.4.1.0/24") & (routes_df["Protocol"] == "ospf")]

image.png

4-3. OSPF確認

OSPFのネイバー関係はospfSessionCompatibility()で確認可能です。こちらもデフォルトでは全機器の情報が表示されますので、.head(5)で最初の5つだけ表示しています。

snippet7
bf.q.ospfSessionCompatibility().answer().frame().head(5)

image.png

4-4. ACL設定確認

上記のようなステータス確認ではなく、Config設定も確認できます。例えばACL設定の場合、definedStructures()types="extended ipv4 access-list line"を指定すれば「IPv4拡張ACL」を表示可能です。

snippet8
bf.q.definedStructures(types="extended ipv4 access-list line").answer().frame()

image.png

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つを表示しています。

snippet9
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の経路で通信できている事が分かります。また、RECEIVEDFORWARDEDTRANSMITTEDでそれぞれ受信、フォワーディング、送信の流れも確認できます。2.でPERMITTEDと記載されている通り、ACL許可されている事も分かります。
image.png

また以下の例では、Reverse_Traces[0][0]で戻りの経路の1つを表示しています。

snippet10
result_bdtr.Reverse_Traces[0][0]

行きの場合、宛先の10.4.1.100を持つ機器が存在しないため結果がDELIVERED_TO_SUBNETとなっていますが、戻りの場合csr1000v-010.1.1.100を持っているためACCEPTEDとなっています。
image.png

(参考) 通信経路確認で用いるQuestion

traceroutereachablityの違いがややこしいのですが、前者は「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を使って正常時の確認を行いました。別の記事で障害時の確認と、正常時・障害時を比較した影響分析も行ってみたいと思います。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?