SDN開発エンジニアを目指した活動ブログ

〜SDNなオープンソース製品を実際に使って試してみる〜

Python / JUNOS環境で、NETCONF動作を試してみる

SDN時代のインフラ構築には、APIベースの設定作業が標準になりつつあります。
通常、仮想VMであれば、OpenStackなどが活用されるところでしょう。
SDNインフラ環境でも既存ネットワークとの相互運用は必須になりますが、NW機器での諸設定が従来通りの手作業によるコンフィグ設定が行なわれている事例がまだ多いと思います。
昨今のSDN技術の台頭により、作業効率の向上、および、オペミス防止の観点から、従来のNW機器もSDNオーケストレーション的なアプローチでAPIベースで制御してしまおうという風潮が高まりつつあります。
そこで、 NETCONFというプロトコルが注目されるわけですね。

◼️ NETCONF確認用のSRXトポロジ環境

今回は、NETCONFを活用したSRX実機でのBGP設定にチャレンジしたいと思います。
なお、BGP構成は、以前の記事と全く同じ構成にします。
なお、NETCONFサーバ動作環境の構築方法は、以前のブログ記事「vSRXを活用した、JUNOSのお勉強まとめ【JUNOS初期設定 + BGP基本編】」と同じです。
SRX実機も、vSRXも同じJUNOSなので、環境構築上の違いはないです。詳しくは、こちらの参照ください。
ttsubo.hatenablog.com

今回は、さらに、NETCONF動作環境を追加しているイメージです。

                   +----------+
                   | -MacOS X-|
                   | NETCONF  |
                   | (Client) |
                   +----------+
                        +
                        |
                        | 192.168.100.0/24
                        |
                        + .101
                   +----------+                          +----------+
                .1 | -SRX100- | .1      i-BGP         .2 | -BHR4GRV-| .1
... -------------+ | NETCONF  | +----------------------+ | OpenWRT  | +------------- ...
    172.16.0.0/24  | (Server) |      192.168.0.0/30      | (Quagga) |  172.16.1.0/24
                   +----------+                          +----------+
                   < AS65000 >                           < AS65000 >

SRXトポロジj構成として、SRX実機に加えて、対向BGPルータを用意する必要があります。
ただ、環境構築になるべくお金を掛けたくなかったので、対向BGPには、Buffalo"BHR-4GRV"に、OpenWRT環境をセットアップした上で、Quaggaをインストールして代用しました。
ttsubo.hatenablog.com

◼️ NETCONF環境を設定する

1. NETCONFクライアント側の環境設定

SDNオーケストレーションなどの自動制御を担う動作環境で、NETCONFを動作させるには、
オープンソース"ncclient: Python library for NETCONF clients"が活用されることが多いようです。
github.com

ncclientは、次のような多種多様なNETCONF装置に対応しているようです。

Supported device handlers

  • Juniper: device_params={'name':'junos'}
  • Cisco CSR: device_params={'name':'csr'}
  • Cisco Nexus: device_params={'name':'nexus'}
  • Huawei: device_params={'name':'huawei'}
  • Alcatel Lucent: device_params={'name':'alu'}
  • H3C: device_params={'name':'h3c'}
  • HP Comware: device_params={'name':'hpcomware'}

ncclient/README.rst at master · ncclient/ncclient · GitHub

(1) 事前準備

まず、ncclientをインストールします。

$ pip install ncclient
(2) BGP設定サンプルアプリ[create-bgp.py]の配置

NETCONFを活用して、BGPコンフィグ設定を行うサンプルアプリを配置します。

from ncclient import manager
from ncclient.xml_ import *
import time

def connect(host, port, user, password, config):
    conn = manager.connect(host=host,
            port=port,
            username=user,
            password=password,
            timeout=30,
            device_params = {'name':'junos'},
            hostkey_verify=False)

    print "------------"
    print "1. conn.lock"
    print "------------"
    try:
        lock = conn.lock('candidate')
        print lock.tostring
    except Exception as e:
        print ("Error in 'conn.lock': type=[%s], message=[%s]"%(type(e), e.message))
        raise

    print "--------------------------"
    print "2. conn.load_configuration"
    print "--------------------------"
    try:
        load_conf = conn.load_configuration(target="candidate",
                                            config=config["routing_options"],
                                            format='xml')
        print load_conf.tostring
        load_conf = conn.load_configuration(target="candidate",
                                            config=config["protocols"],
                                            format='xml')
        print load_conf.tostring
    except Exception as e:
        print ("Error in 'conn.load_configuration': type=[%s], message=[%s]"%(type(e), e.message))
        raise

    print "----------------"
    print "3. conn.validate"
    print "----------------"
    try:
        validate = conn.validate()
        print validate.tostring
    except Exception as e:
        print ("Error in 'conn.validate': type=[%s], message=[%s]"%(type(e), e.message))
        raise

    print "-----------------------------"
    print "4. conn.compare_configuration"
    print "-----------------------------"
    try:
        compare = conn.compare_configuration()
        print compare.tostring
    except Exception as e:
        print ("Error in 'conn.compare_configuration': type=[%s], message=[%s]"%(type(e), e.message))
        raise

    print "---------------"
    print "5. conn.commit"
    print "---------------"
    try:
        commit = conn.commit()
        print commit.tostring
    except Exception as e:
        print ("Error in 'conn.commit': type=[%s], message=[%s]"%(type(e), e.message))
        raise

    print "---------------"
    print "6. conn.unlock"
    print "---------------"
    try:
        unlock = conn.unlock()
        print unlock.tostring
    except Exception as e:
        print ("Error in 'conn.unlock': type=[%s], message=[%s]"%(type(e), e.message))
        raise


if __name__ == '__main__':

    protocols = new_ele('protocols')
    bgp = sub_ele(protocols, 'bgp')
    group = sub_ele(bgp, 'group')
    group_name = sub_ele(group, 'name').text = 'INTERNAL'
    sub_ele(group, 'type').text = 'internal'
    sub_ele(group, 'export').text = 'export-bgp'
    sub_ele(group, 'neighbor').text = '192.168.0.2'

    routing_options = new_ele('routing-options')
    sub_ele(routing_options, 'router-id').text = '10.0.0.1'
    sub_ele(routing_options, 'autonomous-system').text = '65000'

    config = {}
    config["routing_options"] = routing_options
    config["protocols"] = protocols

    connect('192.168.100.101', 830, 'tsubo', 'xxxxxxx', config)
(3) BGP確認サンプルアプリ[show-bgp.py]の配置

NETCONFを活用して、BGP動作確認を行うサンプルアプリを配置します。

from ncclient import manager
from ncclient.xml_ import *
import time

def connect(host, port, user, password):
    conn = manager.connect(host=host,
            port=port,
            username=user,
            password=password,
            timeout=30,
            device_params = {'name':'junos'},
            hostkey_verify=False)

    print "-------------------------------------"
    print "1. conn.get_configuration 'protocols'"
    print "-------------------------------------"
    config_filter = new_ele('configuration')
    sub_ele(config_filter, 'routing-options')
    sub_ele(config_filter, 'protocols')
    get_conf = conn.get_configuration(format='text', filter=config_filter)
    print get_conf.tostring

    print "----------------------------------"
    print "2. conn.command 'show bgp summary'"
    print "----------------------------------"
    time.sleep(10)
    print "root@SRX> show bgp summary"
    result = conn.command(command='show bgp summary', format='text')
    print result.xpath('output')[0].text

    print "-------------------------------------------------------------"
    print "3. conn.command 'show route receive-protocol bgp 192.168.0.2'"
    print "-------------------------------------------------------------"
    print "root@SRX> show route receive-protocol bgp 192.168.0.2"
    result = conn.command(command='show route receive-protocol bgp 192.168.0.2', format='text')
    print result.xpath('output')[0].text


if __name__ == '__main__':

    connect('192.168.100.101', 830, 'tsubo', 'xxxxxxx')

2. NETCONFサーバ側の環境設定

NETCONFサーバ環境として、JUNOS搭載のNW機器が活用されることが多いと思います、
そこで、今回は、JUNOS搭載のNETCONFサーバ環境として、SRX実機を採用しました。
f:id:ttsubo:20160416174416j:plain

(1) 事前準備

NETCONFクライアント環境からの制御を受信できるように準備しておきます。
具体的には、NETCONFプロトコル(830ポート)を有効にしておきます。

root@SRX> configure 
Entering configuration mode

[edit]
root@SRX# set system services netconf ssh port 830
(2) その他、事前に行っておくコンフィグ設定

今回のNETCONF動作として、"protocols"と"routing-options "を設定することにします。
従って、それ以外の各種コンフィグ設定は、事前に設定しておきます。

root@SRX> show configuration |display set    
set version 12.1X44-D35.5
set system host-name SRX
set system time-zone Asia/Tokyo
set system root-authentication encrypted-password "$1$kAd32lTR$F7o4dwAbW9vY1CIV.a9kt."
set system login user tsubo uid 2000
set system login user tsubo class super-user
set system login user tsubo authentication encrypted-password "$1$eT7nqVkt$uCwT9W8WuBhrEhBcyKoQ5/"
set system services ssh root-login allow
set system services netconf ssh port 830
set system syslog archive size 100k
set system syslog archive files 3
set system syslog user * any emergency
set system syslog file messages any any
set system syslog file messages authorization info
set system syslog file interactive-commands interactive-commands any
set system max-configurations-on-flash 5
set system max-configuration-rollbacks 5
set system license autoupdate url https://ae1.juniper.net/junos/key_retrieval
set system ntp
set interfaces fe-0/0/0 unit 0 family inet address 192.168.100.101/24
set interfaces fe-0/0/1 unit 0 family inet address 192.168.0.1/24
set interfaces fe-0/0/2 unit 0 family inet address 172.16.0.1/24
set interfaces lo0 unit 0 family inet address 10.0.0.1/32
set policy-options policy-statement export-bgp term 1 from route-filter 172.16.0.0/24 exact
set policy-options policy-statement export-bgp term 1 then accept
set security forwarding-options family inet6 mode packet-based
set security forwarding-options family mpls mode packet-based

◼️ いよいよ、NETCONF動作を試してみる

1. BGP設定を行う

まずは、NETCONFを活用して、"routing-options"と"protocols"のコンフィグ設定を行います。

$ python create-bgp.py 
------------
1. conn.lock
------------
<rpc-reply message-id="urn:uuid:06ced48c-03d4-11e6-81aa-a45e60ba8c55">
  <ok/>
</rpc-reply>

--------------------------
2. conn.load_configuration
--------------------------
<rpc-reply message-id="urn:uuid:06e4c133-03d4-11e6-ab26-a45e60ba8c55">
  <load-configuration-results>
    <ok/>
  </load-configuration-results>
</rpc-reply>

<rpc-reply message-id="urn:uuid:06f798fd-03d4-11e6-afa3-a45e60ba8c55">
  <load-configuration-results>
    <ok/>
  </load-configuration-results>
</rpc-reply>

----------------
3. conn.validate
----------------
<rpc-reply message-id="urn:uuid:070b022b-03d4-11e6-bca1-a45e60ba8c55">
  <commit-results>
</commit-results>
  <ok/>
</rpc-reply>

-----------------------------
4. conn.compare_configuration
-----------------------------
<rpc-reply message-id="urn:uuid:0a753c47-03d4-11e6-8b4f-a45e60ba8c55">
  <configuration-information>
    <configuration-output>
[edit]
+  routing-options {
+      router-id 10.0.0.1;
+      autonomous-system 65000;
+  }
+  protocols {
+      bgp {
+          group INTERNAL {
+              type internal;
+              export export-bgp;
+              neighbor 192.168.0.2;
+          }
+      }
+  }
</configuration-output>
  </configuration-information>
</rpc-reply>

---------------
5. conn.commit
---------------
<rpc-reply message-id="urn:uuid:0b0273cc-03d4-11e6-a844-a45e60ba8c55">
  <ok/>
</rpc-reply>

---------------
6. conn.unlock
---------------
<rpc-reply message-id="urn:uuid:1663174f-03d4-11e6-bf75-a45e60ba8c55">
  <ok/>
</rpc-reply>

特に、エラーも発生せずに、BGP設定を行うことができたようです。

2. BGP動作を確認してみる

それでは、NETCONFを活用して、"routing-options"と"protocols"のコンフィグ設定が正しく行われたことを確認してみます。

$ python show-bgp.py 
-------------------------------------
1. conn.get_configuration 'protocols'
-------------------------------------
<rpc-reply message-id="urn:uuid:2119510a-03d4-11e6-9a47-a45e60ba8c55">
  <configuration-text>
## Last changed: 2016-04-16 22:06:42 JST
routing-options {
    router-id 10.0.0.1;
    autonomous-system 65000;
}
## Last changed: 2016-04-16 22:06:42 JST
protocols {
    bgp {
        group INTERNAL {
            type internal;
            export export-bgp;
            neighbor 192.168.0.2;
        }
    }
}
</configuration-text>
</rpc-reply>

----------------------------------
2. conn.command 'show bgp summary'
----------------------------------
root@SRX> show bgp summary

Groups: 1 Peers: 1 Down peers: 0
Table          Tot Paths  Act Paths Suppressed    History Damp State    Pending
inet.0                 1          1          0          0          0          0
Peer                     AS      InPkt     OutPkt    OutQ   Flaps Last Up/Dwn State|#Active/Received/Accepted/Damped...
192.168.0.2           65000          3          4       0       0          18 1/1/1/0              0/0/0/0

-------------------------------------------------------------
3. conn.command 'show route receive-protocol bgp 192.168.0.2'
-------------------------------------------------------------
root@SRX> show route receive-protocol bgp 192.168.0.2


inet.0: 8 destinations, 8 routes (8 active, 0 holddown, 0 hidden)
  Prefix		  Nexthop	       MED     Lclpref    AS path
* 172.16.1.0/24           192.168.0.2          0       100        I

さらに、対向BGPルータにて、SRXから配布されたBGP経路が受信できたことを確認してみます。

Quagga-3# show ip bgp
BGP table version is 0, local router ID is 10.0.1.3
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,
              r RIB-failure, S Stale, R Removed
Origin codes: i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*>i172.16.0.0/24    192.168.0.1                   100      0 i
*> 172.16.1.0/24    0.0.0.0                  0         32768 i

Total number of prefixes 2

以上より、NETCONFを活用することにより、NW機器への各種コンフィグ設定/確認が自動的に実施できるよう、Pythonスクリプト化することができました。

◼️ NETCONFエラー判定動作を確認する

ncclient側でのAPI制御結果として、エラー判定された時のNETCONF動作を確認しておきます。

事例1「他ユーザが、コンフィグ処理中のとき」

例えば、lo0インタフェースのDescriptionの設定に関わるコンフィグ設定が完了しないときに、NETCONFを活用して、BGP設定を行ってみます。

(1) 他ユーザによるDescriptionの設定

Description設定後、commitを行わず、放置しておきます。

root@SRX# set interfaces lo0 description lo0_description 

[edit]
root@SRX# show |compare 
[edit interfaces lo0]
+   description lo0_description;

[edit]
(2) NETCONFによるBGP設定

このとき、NETCONFのlock処理がエラー判定となるので、BGP設定サンプルアプリ[create-bgp.py]が異常終了することを確認します。

$ python create-bgp.py 
------------
1. conn.lock
------------
Error in 'conn.lock': type=[<class 'ncclient.operations.rpc.RPCError'>], message=[
configuration database modified
]
Traceback (most recent call last):
  File "create-bgp.py", line 99, in <module>
    connect('192.168.100.101', 830, 'tsubo', 'xxxxxxxx', config)
  File "create-bgp.py", line 18, in connect
    lock = conn.lock('candidate')
  File "/usr/local/lib/python2.7/site-packages/ncclient/manager.py", line 158, in wrapper
    return self.execute(op_cls, *args, **kwds)
  File "/usr/local/lib/python2.7/site-packages/ncclient/manager.py", line 228, in execute
    raise_mode=self._raise_mode).request(*args, **kwds)
  File "/usr/local/lib/python2.7/site-packages/ncclient/operations/lock.py", line 35, in request
    return self._request(node)
  File "/usr/local/lib/python2.7/site-packages/ncclient/operations/rpc.py", line 336, in _request
    raise self._reply.error
ncclient.operations.rpc.RPCError: 
configuration database modified

期待どおりに、ncclient側でのNETCONF処理でエラー判定された場合には、Python処理がException終了してくれました。

事例2「事前設定のコンフィグ不備のとき」

BGP設定サンプルアプリ[create-bgp.py]では、事前にpolicy-statement "export-bgp"を設定しておく必要があります。
ここでは、あえて、事前に、policy-statementを削除してみます。

(1) policy-statement "export-bgp"のコンフィグ設定不備

policy-statementを削除しておきます。

root@SRX# delete policy-options 

[edit]
root@SRX# show |compare 
[edit]
-  policy-options {
-      policy-statement export-bgp {
-          term 1 {
-              from {
-                  route-filter 172.16.0.0/24 exact;
-              }
-              then accept;
-          }
-      }
-  }

[edit]
root@SRX# commit 
commit complete

[edit]
(2) NETCONFによるBGP設定

このとき、NETCONFのvalidate処理がエラー判定となるので、BGP設定サンプルアプリ[create-bgp.py]が異常終了することを確認します。

$ python create-bgp.py 
------------
1. conn.lock
------------
<rpc-reply message-id="urn:uuid:8853948f-0454-11e6-975b-a45e60ba8c55">
  <ok/>
</rpc-reply>

--------------------------
2. conn.load_configuration
--------------------------
<rpc-reply message-id="urn:uuid:88685466-0454-11e6-a6e1-a45e60ba8c55">
  <load-configuration-results>
    <ok/>
  </load-configuration-results>
</rpc-reply>

<rpc-reply message-id="urn:uuid:887ab21c-0454-11e6-bdd1-a45e60ba8c55">
  <load-configuration-results>
    <ok/>
  </load-configuration-results>
</rpc-reply>

----------------
3. conn.validate
----------------
Error in 'conn.validate': type=[<class 'ncclient.operations.rpc.RPCError'>], message=[error: Policy error: Policy export-bgp referenced but not defined
error: BGP: export list not applied
error: configuration check-out failed]
Traceback (most recent call last):
  File "create-bgp.py", line 99, in <module>
    connect('192.168.100.101', 830, 'tsubo', 'xxxxxxxx', config)
  File "create-bgp.py", line 44, in connect
    validate = conn.validate()
  File "/usr/local/lib/python2.7/site-packages/ncclient/manager.py", line 158, in wrapper
    return self.execute(op_cls, *args, **kwds)
  File "/usr/local/lib/python2.7/site-packages/ncclient/manager.py", line 228, in execute
    raise_mode=self._raise_mode).request(*args, **kwds)
  File "/usr/local/lib/python2.7/site-packages/ncclient/operations/edit.py", line 123, in request
    return self._request(node)
  File "/usr/local/lib/python2.7/site-packages/ncclient/operations/rpc.py", line 334, in _request
    raise RPCError(to_ele(self._reply._raw), errs=errors)
ncclient.operations.rpc.RPCError: error: Policy error: Policy export-bgp referenced but not defined
error: BGP: export list not applied
error: configuration check-out failed

期待どおりに、ncclient側でのNETCONF処理でエラー判定された場合には、Python処理がException終了してくれました。

◼️ 最後に、

SDNインフラと相互接続しているNW機器を、SDNオーケストレーション層から順序制御できる仕組みとして、NETCONFの活用は、とても有効であると思います。
今後も、クラウド基盤として、OpenStackが活用される事例は、ますます増えていくと思います。OpenStackとNETCONFの動作連携を担う仕組みとして、heat プロジェクトの役割りの重要性が、どんどん増していくのでしょう。

vSRXを活用した、JUNOSのお勉強まとめ【mpBGP / MPLS-VPN 続編】

今回も、引き続き、JUNOSのお勉強メモです。
ttsubo.hatenablog.com

前回は、PE - CE間は、Staticルーティングを前提としておりました。
今回は、PE - CE間で、eBGPによるダイナミックルーティングの設定に変更します。

◼︎ vSRXトポロジ構成

だんだんと、トポロジ図が複雑になってしまったので、今回は、OSPF / LDP面の記載を割愛しました。
あと、前回からの差分は、
・pc1の箇所に、VyOS-1を配備
・VyOS-1とvSRX-1間をeBGPに変更

                                       vSRX-1                    vSRX-3
                                     +---------+               +---------+                                               
                                     |         |               |         |                                  
                                     | 10.0.0.1|    vSRX-2     | 10.0.0.3|
                                     | +-----+ |    +----+     | +-----+ |
                                     | | GRT +-------------------+ GRT | |             
                                     | +--+--+ |    +----+     | +--+--+ |
                                     |    |    |               |    |    |
20.0.0.1/24 +--------+ 172.16.0.0/30 | +--+--+ |               | +--+--+ | 172.16.1.0/30 +-----+ 10.0.0.1/24
20.0.1.1/24 | VyOS-1 +-----------------+ VRF | |               | | VRF +-----------------+ pc2 | 10.0.1.1/24
            +--------+ .1         .2 | +-----+ |               | +-----+ | .1        .2  +-----+ 10.0.2.1/24
             AS: 65001               +---------+               +---------+
                                      AS: 65000                 AS: 65000  

                     :               :         :               :         :               :
                     : < --------- > :         : < --------- > :         : < --------- > :
                           eBGP                     mp-iBGP                    static

◼︎ JUNOSルーティング設定

(0) 事前準備

前回の記事で設定したダイレクト接続経路"172.16.0.0/30"を再配布するコンフィグ設定を、削除しておきます。
それ以外のコンフィグ設定は、前回のまま活用することとします。

root@vSRX-1# delete routing-instances VPN-A vrf-table-label 
root@vSRX-1# commit 

(1) VRFルーティングインスタンス基本設定

vSRX-1側でのコンフィグは、概ね、前回を設定を踏襲します。唯一、VRF内部で、eBGP Peerを動作させる設定のみ追加します。
なお、vSRX-3側でのコンフィグは、全く変更の必要がありません。
1. routing-instancesを設定する

root@vSRX-1# set routing-instances VPN-A protocols bgp group VPN-A-BGP type external 
root@vSRX-1# set routing-instances VPN-A protocols bgp group VPN-A-BGP peer-as 65001    
root@vSRX-1# set routing-instances VPN-A protocols bgp group VPN-A-BGP neighbor 172.16.0.1 

[edit]
root@vSRX-1# show | compare 
[edit routing-instances VPN-A]
+    protocols {
+        bgp {
+            group VPN-A-BGP {
+                type external;
+                peer-as 65001;
+                neighbor 172.16.0.1;
+            }
+        }
+    }

root@vSRX-1# commit 

2. BGP Peerの状態を確認しておく

root@vSRX-1> show bgp neighbor 172.16.0.1 
Peer: 172.16.0.1+35346 AS 65001 Local: 172.16.0.2+179 AS 65000
  Type: External    State: Established    Flags: <Sync>
  Last State: OpenConfirm   Last Event: RecvKeepAlive
  Last Error: None
  Options: <Preference PeerAS Refresh>
  Holdtime: 90 Preference: 170
  Number of flaps: 0
  Peer ID: 172.16.0.1      Local ID: 172.16.0.2        Active Holdtime: 90
  Keepalive Interval: 30         Group index: 1    Peer index: 0   
  BFD: disabled, down
  Local Interface: ge-0/0/0.0                       
  NLRI for restart configured on peer: inet-unicast
  NLRI advertised by peer: inet-unicast
  NLRI for this session: inet-unicast
  Peer supports Refresh capability (2)
  Stale routes from peer are kept for: 300
  Peer does not support Restarter functionality
  Peer does not support Receiver functionality
  Peer supports 4 byte AS extension (peer-as 65001)
  Peer does not support Addpath
  Table VPN-A.inet.0 Bit: 20001
    RIB State: BGP restart is complete
    RIB State: VPN restart is complete
    Send state: in sync
    Active prefixes:              2
    Received prefixes:            2
    Accepted prefixes:            2
    Suppressed due to damping:    0
    Advertised prefixes:          4
  Last traffic (seconds): Received 21   Sent 5    Checked 51  
  Input messages:  Total 10     Updates 2       Refreshes 0     Octets 268
  Output messages: Total 11     Updates 1       Refreshes 0     Octets 301
  Output Queue[1]: 0            (VPN-A.inet.0, inet-unicast)

3. VPN-Aのアドバタイズ経路を確認しておく
対向側(VyOS-1)からアドバタイズされた経路を受信できるようになりました。

root@vSRX-1> show route receive-protocol bgp 172.16.0.1 table VPN-A.inet.0 

VPN-A.inet.0: 8 destinations, 8 routes (8 active, 0 holddown, 0 hidden)
  Prefix                  Nexthop              MED     Lclpref    AS path
* 20.0.0.0/24             172.16.0.1           1                  65001 I
* 20.0.1.0/24             172.16.0.1           1                  65001 I

つづいて、対向側(VyOS-1)へのアドバイズを確認してみました。
通常のBGP設定と異なり、mpBGP/MPLS-VPN設定の場合は、Export policy適用せずとも、アドバタイズされるようです。

root@vSRX-1> show route advertising-protocol bgp 172.16.0.1 

VPN-A.inet.0: 8 destinations, 8 routes (8 active, 0 holddown, 0 hidden)
  Prefix                  Nexthop              MED     Lclpref    AS path
* 10.0.0.0/24             Self                                    I
* 10.0.1.0/24             Self                                    I
* 10.0.2.0/24             Self                                    I
* 172.16.1.0/30           Self                                    I

vSRX-1でのVRFテーブルの保持状態を確認してみます。

root@vSRX-1> show route table VPN-A.inet.0 

VPN-A.inet.0: 8 destinations, 8 routes (8 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

10.0.0.0/24        *[BGP/170] 03:13:11, localpref 100, from 10.0.0.3
                      AS path: I, validation-state: unverified
                    > to 192.168.0.2 via ge-0/0/1.0, Push 299856, Push 299792(top)
10.0.1.0/24        *[BGP/170] 03:13:11, localpref 100, from 10.0.0.3
                      AS path: I, validation-state: unverified
                    > to 192.168.0.2 via ge-0/0/1.0, Push 299856, Push 299792(top)
10.0.2.0/24        *[BGP/170] 03:13:11, localpref 100, from 10.0.0.3
                      AS path: I, validation-state: unverified
                    > to 192.168.0.2 via ge-0/0/1.0, Push 299856, Push 299792(top)
20.0.0.0/24        *[BGP/170] 00:13:36, MED 1, localpref 100
                      AS path: 65001 I, validation-state: unverified
                    > to 172.16.0.1 via ge-0/0/0.0
20.0.1.0/24        *[BGP/170] 00:13:36, MED 1, localpref 100
                      AS path: 65001 I, validation-state: unverified
                    > to 172.16.0.1 via ge-0/0/0.0
172.16.0.0/30      *[Direct/0] 06:00:41
                    > via ge-0/0/0.0
172.16.0.2/32      *[Local/0] 06:00:41
                      Local via ge-0/0/0.0
172.16.1.0/30      *[BGP/170] 03:13:11, localpref 100, from 10.0.0.3
                      AS path: I, validation-state: unverified
                    > to 192.168.0.2 via ge-0/0/1.0, Push 299856, Push 299792(top)

(2) エンド端末間での疎通確認

まず、VyOS-1側でのBGPテーブルを確認しておきます。

vyos# show ip bgp  
BGP table version is 0, local router ID is 172.16.0.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,
              r RIB-failure, S Stale, R Removed
Origin codes: i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*> 10.0.0.0/24      172.16.0.2                             0 65000 i
*> 10.0.1.0/24      172.16.0.2                             0 65000 i
*> 10.0.2.0/24      172.16.0.2                             0 65000 i
*> 20.0.0.0/24      0.0.0.0                  1         32768 i
*> 20.0.1.0/24      0.0.0.0                  1         32768 i
*> 172.16.1.0/30    172.16.0.2                             0 65000 i

Total number of prefixes 6

エンドエンド端末間でpingをうってみましょう。

1. "172.16.1.2"宛にpingをうってみる

vyos# ping 172.16.1.2
PING 172.16.1.2 (172.16.1.2) 56(84) bytes of data.
64 bytes from 172.16.1.2: icmp_req=1 ttl=61 time=0.624 ms
64 bytes from 172.16.1.2: icmp_req=2 ttl=61 time=1.69 ms
64 bytes from 172.16.1.2: icmp_req=3 ttl=61 time=0.810 ms
64 bytes from 172.16.1.2: icmp_req=4 ttl=61 time=0.700 ms
64 bytes from 172.16.1.2: icmp_req=5 ttl=61 time=0.747 ms
^C
--- 172.16.1.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3999ms
rtt min/avg/max/mdev = 0.624/0.914/1.690/0.393 ms

pingが成功しました!

2. "10.0.0.1"宛にpingをうってみる

vyos# ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_req=1 ttl=61 time=0.735 ms
64 bytes from 10.0.0.1: icmp_req=2 ttl=61 time=0.826 ms
64 bytes from 10.0.0.1: icmp_req=3 ttl=61 time=0.834 ms
64 bytes from 10.0.0.1: icmp_req=4 ttl=61 time=0.716 ms
64 bytes from 10.0.0.1: icmp_req=5 ttl=61 time=0.921 ms
^C
--- 10.0.0.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3996ms
rtt min/avg/max/mdev = 0.716/0.806/0.921/0.078 ms

こちらも、pingが成功しました!

◼︎ 最終的なコンフィグ内容の確認

1. vSRX-1側コンフィグ内容

root@vSRX-1> show configuration 
... (snip)
routing-options {
    router-id 10.0.0.1;
    autonomous-system 65000;
}
protocols {
    mpls {
        interface ge-0/0/1.0;
    }
    bgp {
        group L3VPN {
            type internal;
            local-address 10.0.0.1;
            family inet-vpn {
                unicast;
            }
            neighbor 10.0.0.3;
        }
    }
    ospf {
        area 0.0.0.0 {
            interface ge-0/0/1.0;
            interface lo0.0 {
                passive;
            }
        }
    }
    ldp {
        interface ge-0/0/1.0;
    }
}
routing-instances {
    VPN-A {
        instance-type vrf;
        interface ge-0/0/0.0;           
        route-distinguisher 65000:101;
        vrf-target target:65000:101;
        protocols {
            bgp {
                group VPN-A-BGP {
                    type external;
                    peer-as 65001;
                    neighbor 172.16.0.1;
                }
            }
        }
    }
}

2. VyOS-1側コンフィグ内容

vyos# show running-config  
Building configuration...

Current configuration:
!
log syslog
log facility local7
!
debug ospf6 lsa unknown
!
interface eth0
 ipv6 nd suppress-ra
 link-detect
!
interface eth1
 ip address 172.16.0.1/30
 ipv6 nd suppress-ra
 link-detect
!
interface eth2
 ip address 20.0.0.1/24
 ipv6 nd suppress-ra
 link-detect
!
interface eth3
 ip address 20.0.1.1/24
 ipv6 nd suppress-ra
 link-detect
!
interface lo
!
router bgp 65001
 bgp router-id 172.16.0.1
 bgp network import-check
 network 20.0.0.0/24
 network 20.0.1.0/24
 neighbor 172.16.0.2 remote-as 65000
!
ip forwarding
ipv6 forwarding
!
line vty
!
end

◼︎ 最後に

mpBGP /MPLS-VPN構成における"GRT"、"VRF"の関係と、おのおので動作するBGP動作がイメージできれば、JUNOSコンフィグの階層構造は、とても理解しやすいものであると感じました。
以上です。

vSRXを活用した、JUNOSのお勉強まとめ【mpBGP / MPLS-VPN編】

今回も、引き続き、JUNOSのお勉強メモです。
ttsubo.hatenablog.com

◼︎ 前回の記事での技術課題

エンド端末間での疎通確認において、エンド端末"pc1"から、エンド端末"pc2"側"10.0.0.1"に対して、pingをうってみたところ、
pingは成功した。この時、pc2側でパケットキャプチャを行ったところ、ICMP Echo Request / Replyが観測されなかった。
「誰がICMP Echo Replyを送信したのだろうか?」というものでした。

まず、こちらのトポロジ構成をご覧ください。
BGP運用経験の豊富な方は、すでに発生事象の原因にお気づきだと思います。

                     [ AS: 65000 ]                                       [ AS: 65000 ]

                       10.0.0.1                   10.0.0.2                  10.0.0.3
                      +---------+                +--------+                +---------+                                   
                      |         |                |        |                |         |
                      | vSRX-1  |                | vSRX-2 |                | vSRX-3  |
+-----+ 172.16.0.0/30 | +-----+ | 192.168.0.0/30 |        | 192.168.1.0/30 | +-----+ | 172.16.1.0/30 +-----+ 10.0.0.1/24
| pc1 +-----------------+ GRT +----------------+ |        | +----------------+ GRT +-----------------+ pc2 | 10.0.1.1/24
+-----+ .1         .2 | +-----+ | .1          .2 |        | .1          .2 | +-----+ | .1        .2  +-----+ 10.0.2.1/24
                      |         |                |        |                |         |
                      +---------+                +--------+                +---------+

      :               :         :                :        :                :         :               :
      : < --------- > :         : < ---------- > :        : < ---------- > :         : < --------- > :
           static               :    LDP(ospf)                 LDP(ospf)   :               static
                                :                                          :
                                :                                          :
                                : < ------------------------------------ > :
                                                    iBGP

vSRX-1側での追跡調査
まず、vSRX-1で保持しているルーティング情報を確認してみましょう。

root@vSRX-1> show route table inet.0    

inet.0: 13 destinations, 13 routes (13 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

10.0.0.0/24        *[BGP/170] 03:08:48, localpref 100, from 10.0.0.3
                      AS path: I, validation-state: unverified
                    > to 192.168.0.2 via ge-0/0/1.0, Push 299792
10.0.0.1/32        *[Direct/0] 07:51:02
                    > via lo0.0
10.0.0.2/32        *[OSPF/10] 04:12:50, metric 1
                    > to 192.168.0.2 via ge-0/0/1.0
10.0.0.3/32        *[OSPF/10] 04:12:50, metric 2
                    > to 192.168.0.2 via ge-0/0/1.0
10.0.1.0/24        *[BGP/170] 03:08:48, localpref 100, from 10.0.0.3
                      AS path: I, validation-state: unverified
                    > to 192.168.0.2 via ge-0/0/1.0, Push 299792
10.0.2.0/24        *[BGP/170] 03:08:48, localpref 100, from 10.0.0.3
                      AS path: I, validation-state: unverified
                    > to 192.168.0.2 via ge-0/0/1.0, Push 299792
172.16.0.0/30      *[Direct/0] 06:30:00
                    > via ge-0/0/0.0
172.16.0.2/32      *[Local/0] 06:30:00
                      Local via ge-0/0/0.0
172.16.1.0/30      *[BGP/170] 03:25:48, localpref 100, from 10.0.0.3
                      AS path: I, validation-state: unverified
                    > to 192.168.0.2 via ge-0/0/1.0, Push 299792
192.168.0.0/30     *[Direct/0] 06:32:29
                    > via ge-0/0/1.0
192.168.0.1/32     *[Local/0] 06:32:29
                      Local via ge-0/0/1.0
192.168.1.0/30     *[OSPF/10] 04:12:50, metric 2
                    > to 192.168.0.2 via ge-0/0/1.0
224.0.0.5/32       *[OSPF/10] 04:13:05, metric 1
                      MultiRecv

はい、お察しの通りでしたね。
vSRXでのルーティング情報は、グローバルルーティングテーブル(GRT)で全て保持することになります。
このGRTでは、OSPF, BGPなどのルーティングプロトコルで学習したルーティング情報に加えて、スタティック経路や、ダイレクト接続された経路も保持することになります。
そして、vSRX-1では、pc1から10.0.0.1”宛のIPパケットを受信した際には、このGRTをルックアップして、次の転送先が決定されますが、この場合だと、ループバックインタフェースが選定されます。
最終的に、vSRX-1のルップバックインタフェースが、ICMP Echo Replyを送信することになります。
これが、前回記事での技術課題の発生原因です。

課題解決を目指して ... mpBGP/ MPLS-VPM化
そもそも、エンド端末を使う人の立場から述べると、
「エンド端末"pc1", "pc2"間で通信できればよくて、vSRX側で構成されるバックボーンネットワークと通信したいわけではない。」
と主張されるのでしょう。

この技術課題を解決する手段として、mpBGP / MPLS-VPN適用が有望だと思います。
そこで、今回のJUNOSのお勉強の素材として、mpBGP / MPLS-VPN対応に着手してみます。

◼︎ vSRXトポロジ構成

前回と同様のトポロジ構成になっています。このトポロジ構成の特徴は、mpBGP / MPLS-VPN対応です。
従って、GRT / VRFの関係がわかりやすいよう記載しました。

                     [ AS: 65000 ]                                       [ AS: 65000 ]

                       10.0.0.1                   10.0.0.2                  10.0.0.3
                      +---------+                +--------+                +---------+                                   
                      |         |                |        |                |         |                      
                      | vSRX-1  |                | vSRX-2 |                | vSRX-3  |
                      | +-----+ | 192.168.0.0/30 |        | 192.168.1.0/30 | +-----+ |
                      | | GRT +----------------+ |        | +----------------+ GRT | | 
                      | +--+--+ | .1          .2 |        | .1          .2 | +--+--+ |
                      |    |    |                |        |                |    |    |
+-----+ 172.16.0.0/30 | +--+--+ |                |        |                | +--+--+ | 172.16.1.0/30 +-----+ 10.0.0.1/24
| pc1 +-----------------+ VRF | |                |        |                | | VRF +-----------------+ pc2 | 10.0.1.1/24
+-----+ .1         .2 | +-----+ |                |        |                | +-----+ | .1        .2  +-----+ 10.0.2.1/24
                      +---------+                +--------+                +---------+

      :               :         :                :        :                :         :               :
      : < --------- > :         : < ---------- > :        : < ---------- > :         : < --------- > :
           static               :    LDP(ospf)                 LDP(ospf)   :               static
                                :                                          :
                                :                                          :
                                : < ------------------------------------ > :
                                                   mp-iBGP

ここでのポイントは、エンド端末を収容するvSRXルータは、ProviderEdge(PE)ルータの役割を担うということです。
なお、PEルータでは、GRT, VRFテーブルをおのおの保持しており、エンド端末との間で共有すべきルーティング情報は、VRFテーブルで保持することになります。
PEルータ間でのエンド端末に関わるBGP経路情報を共有する際には、GRTテーブルを介してmp-BGP UPDATEメッセージでやりとりが行われます。そして、最終的にVRFテーブルに保持されます。

◼︎ JUNOSルーティング設定

mpBGP / MPLS-VPNに関わる各種ルーティング設定を行います。

(0) 事前準備

前回の記事で設定したBGP関連のコンフィグ設定を、全て削除しておきます。
ただし、OSPF, LDPのコンフィグ設定は、前回のまま活用することとします。

root@vSRX-1# delete protocols bgp     
root@vSRX-1# delete policy-options 

[edit]
root@vSRX-1# show | compare 
[edit protocols]
-   bgp {
-       group INTERNAL {
-           type internal;
-           local-address 10.0.0.1;
-           export export-bgp;
-           neighbor 10.0.0.3;
-       }
-   }
[edit]
-  policy-options {
-      policy-statement export-bgp {
-          term 1 {
-              from {
-                  route-filter 172.16.0.0/30 exact;
-              }
-              then accept;
-          }
-      }
-  }

root@vSRX-1# commit 

vSRX-3では、スタティック経路の設定も削除しておきます。

root@vSRX-3# delete routing-options static

(1) mpBGP / MPLS-VPN基本設定

1. routing-instancesを設定する

root@vSRX-1# set routing-instances VPN-A instance-type vrf  
root@vSRX-1# set routing-instances VPN-A interface ge-0/0/0.0 
root@vSRX-1# set routing-instances VPN-A route-distinguisher 65000:101 
root@vSRX-1# set routing-instances VPN-A vrf-target target:65000:101
root@vSRX-1# show | compare                                            
[edit]
+  routing-instances {
+      VPN-A {
+          instance-type vrf;
+          interface ge-0/0/0.0;
+          route-distinguisher 65000:101;
+          vrf-target target:65000:101;
+      }
+  }

root@vSRX-1# commit

2. protocolsを設定する

root@vSRX-1# set protocols bgp group L3VPN type internal
root@vSRX-1# set protocols bgp group L3VPN local-address 10.0.0.1
root@vSRX-1# set protocols bgp group L3VPN neighbor 10.0.0.3
root@vSRX-1# set protocols bgp group L3VPN family inet-vpn unicast
root@vSRX-1# show | compare 
[edit protocols]
+   bgp {
+       group L3VPN {
+           type internal;
+           local-address 10.0.0.1;
+           family inet-vpn {
+               unicast;
+           }
+           neighbor 10.0.0.3;
+       }
+   }

root@vSRX-1# commit 

3. static routeを設定する
vSRX-3側では、pc2配下のスタティック経路をエントリしておく必要があります。

set routing-instances VPN-A routing-options static route 10.0.0.0/24 next-hop 172.16.1.2
set routing-instances VPN-A routing-options static route 10.0.1.0/24 next-hop 172.16.1.2
set routing-instances VPN-A routing-options static route 10.0.2.0/24 next-hop 172.16.1.2
root@vSRX-3# show | compare                                                                              
[edit routing-instances VPN-A routing-options static]
+      route 10.0.0.0/24 next-hop 172.16.1.2;
+      route 10.0.1.0/24 next-hop 172.16.1.2;
+      route 10.0.2.0/24 next-hop 172.16.1.2;

root@vSRX-3# commit

4. BGP Peerの状態を確認しておく

root@vSRX-1> show bgp neighbor 
Peer: 10.0.0.3+50824 AS 65000  Local: 10.0.0.1+179 AS 65000
  Type: Internal    State: Established    Flags: <Sync>
  Last State: OpenConfirm   Last Event: RecvKeepAlive
  Last Error: None
  Options: <Preference LocalAddress AddressFamily Rib-group Refresh>
  Address families configured: inet-vpn-unicast
  Local Address: 10.0.0.1 Holdtime: 90 Preference: 170
  Number of flaps: 0
  Peer ID: 10.0.0.3        Local ID: 10.0.0.1          Active Holdtime: 90
  Keepalive Interval: 30         Group index: 0    Peer index: 0   
  BFD: disabled, down
  NLRI for restart configured on peer: inet-vpn-unicast
  NLRI advertised by peer: inet-vpn-unicast
  NLRI for this session: inet-vpn-unicast
  Peer supports Refresh capability (2)
  Stale routes from peer are kept for: 300
  Peer does not support Restarter functionality
  NLRI that restart is negotiated for: inet-vpn-unicast
  NLRI of received end-of-rib markers: inet-vpn-unicast
  NLRI of all end-of-rib markers sent: inet-vpn-unicast
  Peer supports 4 byte AS extension (peer-as 65000)
  Peer does not support Addpath
  Table bgp.l3vpn.0
    RIB State: BGP restart is complete
    RIB State: VPN restart is complete
    Send state: not advertising
    Active prefixes:              4
    Received prefixes:            4
    Accepted prefixes:            4
    Suppressed due to damping:    0
  Table VPN-A.inet.0 Bit: 20000
    RIB State: BGP restart is complete
    RIB State: VPN restart is complete
    Send state: in sync
    Active prefixes:              4
    Received prefixes:            4
    Accepted prefixes:            4
    Suppressed due to damping:    0
    Advertised prefixes:          1
  Last traffic (seconds): Received 16   Sent 22   Checked 32  
  Input messages:  Total 137    Updates 8       Refreshes 0     Octets 2973
  Output messages: Total 136    Updates 7       Refreshes 0     Octets 2913
  Output Queue[1]: 0            (VPN-A.inet.0, inet-vpn-unicast)

5. VPN-Aのアドバタイズ経路を確認しておく
通常のBGP設定と異なり、mpBGP/MPLS-VPN設定の場合は、vSRX-3ルータ側でExport policy適用せずとも、アドバタイズされるようです。
対向側(vSRX-3ルータ)からアドバタイズされた経路を受信できるようになりました。

root@vSRX-1> show route receive-protocol bgp 10.0.0.3 table bgp.l3vpn.0    

bgp.l3vpn.0: 4 destinations, 4 routes (4 active, 0 holddown, 0 hidden)
  Prefix                  Nexthop              MED     Lclpref    AS path
  65000:101:10.0.0.0/24                    
*                         10.0.0.3                     100        I
  65000:101:10.0.1.0/24                    
*                         10.0.0.3                     100        I
  65000:101:10.0.2.0/24                    
*                         10.0.0.3                     100        I
  65000:101:172.16.1.0/30                    
*                         10.0.0.3                     100        I

つづいて、対向側(vSRX-3ルータ)へのアドバイズを確認してみました。
しかしながら、"172.16.0.0/30"は、アドバタイズされていないようです。

root@vSRX-1> show route advertising-protocol bgp 10.0.0.3                  

VPN-A.inet.0: 6 destinations, 6 routes (6 active, 0 holddown, 0 hidden)
  Prefix                  Nexthop              MED     Lclpref    AS path
* 172.16.0.0/30           Not advertised               100        I

ここでのトラブル解析として、各々のvSRXルータでのVRFテーブルの保持状態を確認してみます。
まずは、vSRX-1側から確認します。

root@vSRX-1> show route table VPN-A.inet.0                                 

VPN-A.inet.0: 6 destinations, 6 routes (6 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

10.0.0.0/24        *[BGP/170] 00:43:58, localpref 100, from 10.0.0.3
                      AS path: I, validation-state: unverified
                    > to 192.168.0.2 via ge-0/0/1.0, Push 299808, Push 299792(top)
10.0.1.0/24        *[BGP/170] 00:17:30, localpref 100, from 10.0.0.3
                      AS path: I, validation-state: unverified
                    > to 192.168.0.2 via ge-0/0/1.0, Push 299808, Push 299792(top)
10.0.2.0/24        *[BGP/170] 00:17:30, localpref 100, from 10.0.0.3
                      AS path: I, validation-state: unverified
                    > to 192.168.0.2 via ge-0/0/1.0, Push 299808, Push 299792(top)
172.16.0.0/30      *[Direct/0] 01:21:51
                    > via ge-0/0/0.0
172.16.0.2/32      *[Local/0] 01:21:51
                      Local via ge-0/0/0.0
172.16.1.0/30      *[BGP/170] 00:43:58, localpref 100, from 10.0.0.3
                      AS path: I, validation-state: unverified
                    > to 192.168.0.2 via ge-0/0/1.0, Push 299808, Push 299792(top)

問題なく、VRFテーブルは構築できているようです。

つづいて、vSRX-3側でも確認してみます。

root@vSRX-3> show route table VPN-A.inet.0    

VPN-A.inet.0: 5 destinations, 5 routes (5 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

10.0.0.0/24        *[Static/5] 00:46:45
                    > to 172.16.1.2 via ge-0/0/1.0
10.0.1.0/24        *[Static/5] 00:20:17
                    > to 172.16.1.2 via ge-0/0/1.0
10.0.2.0/24        *[Static/5] 00:20:17
                    > to 172.16.1.2 via ge-0/0/1.0
172.16.1.0/30      *[Direct/0] 01:12:49
                    > via ge-0/0/1.0
172.16.1.1/32      *[Local/0] 01:12:49
                      Local via ge-0/0/1.0

やはり、vSRX-1側でのダイレクト接続経路"172.16.0.0/30"が、VRFテーブルに保持されておりませんね。
どうやら、pc1側では、スタティック経路が存在しないので、next-hopに該当するダイレクト接続経路"172.16.0.0/30"を、敢えて、BGP経路としてアドバタイズしていないようです。

6. ダイレクト接続経路"172.16.0.0/30"を再配布するには、
CiscoIOSの"redistribute connected"コマンドのように、BGP経路に再配布する方法をいろいろ試行錯誤してみましたが、うまく行きません。そこで、不本意ながら、default-routeを再配布する方法で、問題回避を試みました。

root@vSRX-1# set routing-instances VPN-A routing-options static route 0.0.0.0/0 next-hop 172.16.0.1    

[edit]
root@vSRX-1# show | compare 
[edit routing-instances VPN-A]
+    routing-options {
+        static {
+            route 0.0.0.0/0 next-hop 172.16.0.1;
+        }
+    }

root@vSRX-1# commit 

とりあえず、vSRX-1側で、ダイレクト接続経路"172.16.0.0/30"をアドバタイズするようになりました。

root@vSRX-1> show route advertising-protocol bgp 10.0.0.3                  

VPN-A.inet.0: 7 destinations, 7 routes (7 active, 0 holddown, 0 hidden)
  Prefix                  Nexthop              MED     Lclpref    AS path
* 0.0.0.0/0               Self                         100        I
* 172.16.0.0/30           Self                         100        I

改めて、vSRX-3側でのVRFテーブルの保持状態を確認してみます。

root@vSRX-3> show route table VPN-A.inet.0    

VPN-A.inet.0: 7 destinations, 7 routes (7 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

0.0.0.0/0          *[BGP/170] 00:03:03, localpref 100, from 10.0.0.1
                      AS path: I, validation-state: unverified
                    > to 192.168.1.1 via ge-0/0/0.0, Push 299824, Push 299776(top)
10.0.0.0/24        *[Static/5] 00:57:51
                    > to 172.16.1.2 via ge-0/0/1.0
10.0.1.0/24        *[Static/5] 00:31:23
                    > to 172.16.1.2 via ge-0/0/1.0
10.0.2.0/24        *[Static/5] 00:31:23
                    > to 172.16.1.2 via ge-0/0/1.0
172.16.0.0/30      *[BGP/170] 00:03:03, localpref 100, from 10.0.0.1
                      AS path: I, validation-state: unverified
                    > to 192.168.1.1 via ge-0/0/0.0, Push 299824, Push 299776(top)
172.16.1.0/30      *[Direct/0] 01:23:55
                    > via ge-0/0/1.0
172.16.1.1/32      *[Local/0] 01:23:55
                      Local via ge-0/0/1.0

今回は、問題なく、VRFテーブルは構築できているようです。

(2) エンド端末間での疎通確認

エンドエンド端末間でpingをうってみましょう。

1. "172.16.1.2"宛にpingをうってみる

tsubo@pc1:~$ ping 172.16.1.2
PING 172.16.1.2 (172.16.1.2) 56(84) bytes of data.
64 bytes from 172.16.1.2: icmp_seq=1 ttl=61 time=0.849 ms
64 bytes from 172.16.1.2: icmp_seq=2 ttl=61 time=0.905 ms
64 bytes from 172.16.1.2: icmp_seq=3 ttl=61 time=0.829 ms
64 bytes from 172.16.1.2: icmp_seq=4 ttl=61 time=0.759 ms
64 bytes from 172.16.1.2: icmp_seq=5 ttl=61 time=0.960 ms
^C
--- 172.16.1.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4001ms
rtt min/avg/max/mdev = 0.759/0.860/0.960/0.073 ms

pingが成功しました!

この時、pc2側でtcpdumpを動作させて、パケットモニタリングしておきます。

root@pc2:~# tcpdump -i eth1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
23:04:48.390124 IP 172.16.0.1 > 172.16.1.2: ICMP echo request, id 2594, seq 1, length 64
23:04:48.390160 IP 172.16.1.2 > 172.16.0.1: ICMP echo reply, id 2594, seq 1, length 64
23:04:49.390956 IP 172.16.0.1 > 172.16.1.2: ICMP echo request, id 2594, seq 2, length 64
23:04:49.390985 IP 172.16.1.2 > 172.16.0.1: ICMP echo reply, id 2594, seq 2, length 64
23:04:50.390871 IP 172.16.0.1 > 172.16.1.2: ICMP echo request, id 2594, seq 3, length 64
23:04:50.390901 IP 172.16.1.2 > 172.16.0.1: ICMP echo reply, id 2594, seq 3, length 64
23:04:51.390847 IP 172.16.0.1 > 172.16.1.2: ICMP echo request, id 2594, seq 4, length 64
23:04:51.390876 IP 172.16.1.2 > 172.16.0.1: ICMP echo reply, id 2594, seq 4, length 64
23:04:52.391451 IP 172.16.0.1 > 172.16.1.2: ICMP echo request, id 2594, seq 5, length 64
23:04:52.391480 IP 172.16.1.2 > 172.16.0.1: ICMP echo reply, id 2594, seq 5, length 64
23:04:53.393256 ARP, Request who-has 172.16.1.1 tell 172.16.1.2, length 28
23:04:53.394175 ARP, Reply 172.16.1.1 is-at 00:0c:29:70:81:a6 (oui Unknown), length 46
^C
12 packets captured
12 packets received by filter
0 packets dropped by kernel

期待通り、ICMP Echo Request / Replyが観測できました。

2. "10.0.0.1"宛にpingをうってみる

tsubo@pc1:~$ ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=61 time=1.68 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=61 time=1.10 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=61 time=0.794 ms
64 bytes from 10.0.0.1: icmp_seq=4 ttl=61 time=0.975 ms
64 bytes from 10.0.0.1: icmp_seq=5 ttl=61 time=0.763 ms
^C
--- 10.0.0.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4005ms
rtt min/avg/max/mdev = 0.763/1.064/1.685/0.334 ms

こちらも、pingが成功しましたね!

この時、pc2側でtcpdumpを動作させて、パケットモニタリングしておきます。

root@pc2:~# tcpdump -i eth1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
23:07:52.149141 IP 172.16.0.1 > 10.0.0.1: ICMP echo request, id 2595, seq 1, length 64
23:07:52.149182 IP 10.0.0.1 > 172.16.0.1: ICMP echo reply, id 2595, seq 1, length 64
23:07:53.150952 IP 172.16.0.1 > 10.0.0.1: ICMP echo request, id 2595, seq 2, length 64
23:07:53.150983 IP 10.0.0.1 > 172.16.0.1: ICMP echo reply, id 2595, seq 2, length 64
23:07:54.152578 IP 172.16.0.1 > 10.0.0.1: ICMP echo request, id 2595, seq 3, length 64
23:07:54.152607 IP 10.0.0.1 > 172.16.0.1: ICMP echo reply, id 2595, seq 3, length 64
23:07:55.151633 IP 172.16.0.1 > 10.0.0.1: ICMP echo request, id 2595, seq 4, length 64
23:07:55.151664 IP 10.0.0.1 > 172.16.0.1: ICMP echo reply, id 2595, seq 4, length 64
23:07:56.153395 IP 172.16.0.1 > 10.0.0.1: ICMP echo request, id 2595, seq 5, length 64
23:07:56.153424 IP 10.0.0.1 > 172.16.0.1: ICMP echo reply, id 2595, seq 5, length 64
23:07:57.152492 ARP, Request who-has 172.16.1.1 tell 172.16.1.2, length 28
23:07:57.153588 ARP, Reply 172.16.1.1 is-at 00:0c:29:70:81:a6 (oui Unknown), length 46
^C
12 packets captured
12 packets received by filter
0 packets dropped by kernel

こちらも、期待通り、ICMP Echo Request / Replyが観測できましたね!
ただし、pc1拠点相当を、さらに追加する場合には、default-routeを再配布する方法は採用できません。
CiscoIOSの"redistribute connected"コマンド相当をサルベージする必要があるのかもしれません。

◼︎ 3/27追記「@kazubuさんから、回避策を教えてもらいました」

こちらが、Direct経路がBGPアドバタイズ経路に乗らない件の回避策になります。

Juniper Networks - BGP not advertising the directly connected network to the remote PE router in L3VPN - Knowledge Base

では、早速、試してみましょう。
1. 暫定的に設定しておいたdefault-routeの再配布の設定を削除する

root@vSRX-1# delete routing-instances VPN-A routing-options static
root@vSRX-1# commit

2. routing-instanceにvrf-table-labelを設定する

root@vSRX-1# set routing-instances VPN-A vrf-table-label    
root@vSRX-1# show | compare 
[edit routing-instances VPN-A]
+    vrf-table-label;

root@vSRX-1# commit

vSRX-1側で、ダイレクト接続経路"172.16.0.0/30"をアドバタイズされるようになりました。

root@vSRX-1> show route advertising-protocol bgp 10.0.0.3             

VPN-A.inet.0: 6 destinations, 6 routes (6 active, 0 holddown, 0 hidden)
  Prefix                  Nexthop              MED     Lclpref    AS path
* 172.16.0.0/30           Self                         100        I

対向側(vSRX-3)でも、ダイレクト接続経路"172.16.0.0/30"が、受信できたことも確認できました。

root@vSRX-3> show route receive-protocol bgp 10.0.0.1 table bgp.l3vpn.0    

bgp.l3vpn.0: 1 destinations, 1 routes (1 active, 0 holddown, 0 hidden)
  Prefix                  Nexthop              MED     Lclpref    AS path
  65000:101:172.16.0.0/30                    
*                         10.0.0.1                     100        I

以下、個人的な所感です。
今まで、「VRFにて保持されるルーティング情報は、一律、BGP Peerにアドバタイズされる。」と理解しておりました。
ところが、通常のMPLSルータでは、「VRFにて保持されるルーティング情報は、next-hopが検出されるタイミングでVPNラベルが付与されるので、BGP Peerへのアドバタイズも可能になる」という仕様なんですね。
ダイレクト接続経路の場合だと、このままでは、next-hopが検出されないので、BGP Peerへのアドバタイズは行われない。
そこで、ダイレクト接続経路に対して、明示的にVPNラベルを付与する必要がある。
その方法は、JUNOSの場合だと、"vhf-table-label"を設定し、CiscoIOSの場合だと、"redistribute connected"を設定する。

ちょっとだけ、賢くなりました。これにて、一件落着という感じです。

◼︎ 最終的なコンフィグ内容の確認

1. vSRX-1側コンフィグ内容

root@vSRX-1> show configuration
... (snip)
routing-options {
    router-id 10.0.0.1;
    autonomous-system 65000;
}
protocols {
    mpls {
        interface ge-0/0/1.0;
    }
    bgp {
        group L3VPN {
            type internal;
            local-address 10.0.0.1;
            family inet-vpn {
                unicast;
            }
            neighbor 10.0.0.3;
        }
    }
    ospf {
        area 0.0.0.0 {
            interface ge-0/0/1.0;
            interface lo0.0 {
                passive;
            }
        }
    }
    ldp {
        interface ge-0/0/1.0;
    }
}
routing-instances {
    VPN-A {
        instance-type vrf;              
        interface ge-0/0/0.0;
        route-distinguisher 65000:101;
        vrf-target target:65000:101;
        vrf-table-label;
    }
}

2. vSRX-3側コンフィグ内容

root@vSRX-3> show configuration 
... (snip)
routing-options {
    router-id 10.0.0.3;
    autonomous-system 65000;
}
protocols {
    mpls {
        interface ge-0/0/0.0;
    }
    bgp {
        group L3VPN {
            type internal;
            local-address 10.0.0.3;
            family inet-vpn {
                unicast;
            }
            neighbor 10.0.0.1;
        }
    }
    ospf {
        area 0.0.0.0 {
            interface ge-0/0/0.0;
            interface lo0.0 {
                passive;
            }
        }
    }
    ldp {
        interface ge-0/0/0.0;
    }
}
routing-instances {
    VPN-A {
        instance-type vrf;              
        interface ge-0/0/1.0;
        route-distinguisher 65000:101;
        vrf-target target:65000:101;
        routing-options {
            static {
                route 10.0.0.0/24 next-hop 172.16.1.2;
                route 10.0.1.0/24 next-hop 172.16.1.2;
                route 10.0.2.0/24 next-hop 172.16.1.2;
            }
        }
    }
}

◼︎ 最後に

今回は、JUNOSによる、mpBGP / MPLS-VPNの基本動作を確認しました。
基本的なJUNOSコマンド操作感は、とてもわかりやすいと感じました。
以上です。

vSRXを活用した、JUNOSのお勉強まとめ【OSPF + MPLS編】

今回も、引き続き、JUNOSのお勉強メモです。
ttsubo.hatenablog.com

◼︎ vSRXトポロジ構成

今回は、vSRX3台構成としました。

                    [ AS: 65000 ]                                       [ AS: 65000 ]

                       10.0.0.1                  10.0.0.2                  10.0.0.3
+-----+               +--------+                +--------+                +--------+               +-----+               
|     | 172.16.0.0/30 |        | 192.168.0.0/30 |        | 192.168.1.0/30 |        | 172.16.1.0/30 |     | 10.0.0.1/24
| pc1 | +-----------+ | vSRX-1 | +------------+ | vSRX-2 | +------------+ | vSRX-3 | +-----------+ | pc2 | 10.0.1.1/24
|     | .1         .2 |        | .1          .2 |        | .1          .2 |        | .1         .2 |     | 10.0.2.1/24
+-----+               +--------+                +--------+                +--------+               +-----+

      :               :        :                :        :                :        :               :
      : < --------- > :        : < ---------- > :        : < ---------- > :        : < --------- > :
           static              :      ospf                      ospf      :              static
                               :                                          :
                               :                                          :
                               : < ------------------------------------ > :
                                                  i-BGP

◼︎ JUNOSルーティング設定

複数台のvSRX間でIP通信が可能なように、各種ルーティング設定を行います。

(1) OSPF基本設定

今回は、すべてのvSRXルータをエリア0に収容する形態としました。

1. protocolsを設定する
まずは、vSRX-1の場合、ge-0/0/1.0 のインタフェースをOSPFエリア0に収容するには、次のようにコンフィグ設定します。
なお、ループバックでOSPFを有効にする場合には、パッシブインタフェースとして設定するのが一般的だそうです。

root@vSRX-1# set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 
root@vSRX-1# set protocols ospf area 0.0.0.0 interface lo0.0 passive 

root@vSRX-1# show | compare    
[edit]
+  protocols {
+      ospf {
+          area 0.0.0.0 {
+              interface ge-0/0/1.0;
+              interface lo0.0 {
+                  passive;
+              }
+          }
+      }
+  }

root@vSRX-1# commit

2. OSPF隣接関係の状態を確認しておく
すべてのvSRXルータでも同様に、OSPFエリア0に収容するインタフェースを有効にします。
vSRX-1で、OSPF隣接状態を確認してみます。
あと、ループバックインタフェースでもOSPFが有効になっていることを確認しておきます。

root@vSRX-1> show ospf neighbor     
Address          Interface              State     ID               Pri  Dead
192.168.0.2      ge-0/0/1.0             Full      10.0.0.2         128    33

root@vSRX-1> show ospf interface    
Interface           State   Area            DR ID           BDR ID          Nbrs
ge-0/0/1.0          BDR     0.0.0.0         10.0.0.2        10.0.0.1           1
lo0.0               DRother 0.0.0.0         0.0.0.0         0.0.0.0            0

そして、vSRX-2でも、OSPF隣接状態を確認してみます。

root@vSRX-2> show ospf neighbor     
Address          Interface              State     ID               Pri  Dead
192.168.0.1      ge-0/0/0.0             Full      10.0.0.1         128    35
192.168.1.2      ge-0/0/1.0             Full      10.0.0.3         128    39

root@vSRX-2> show ospf interface    
Interface           State   Area            DR ID           BDR ID          Nbrs
ge-0/0/0.0          DR      0.0.0.0         10.0.0.2        10.0.0.1           1
ge-0/0/1.0          BDR     0.0.0.0         10.0.0.3        10.0.0.2           1
lo0.0               DRother 0.0.0.0         0.0.0.0         0.0.0.0            0

さらに、vSRX-3でも、OSPF隣接状態を確認しておきます。

root@vSRX-3> show ospf neighbor     
Address          Interface              State     ID               Pri  Dead
192.168.1.1      ge-0/0/0.0             Full      10.0.0.2         128    32

root@vSRX-3> show ospf interface    
Interface           State   Area            DR ID           BDR ID          Nbrs
ge-0/0/0.0          DR      0.0.0.0         10.0.0.3        10.0.0.2           1
lo0.0               DRother 0.0.0.0         0.0.0.0         0.0.0.0            0

最後に、OSPFプロトコルによって学習したルーティング情報を確認しておきます。

root@vSRX-1> show route protocol ospf    

inet.0: 9 destinations, 9 routes (9 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

10.0.0.2/32        *[OSPF/10] 00:21:26, metric 1
                    > to 192.168.0.2 via ge-0/0/1.0
10.0.0.3/32        *[OSPF/10] 00:21:26, metric 2
                    > to 192.168.0.2 via ge-0/0/1.0
192.168.1.0/30     *[OSPF/10] 00:21:26, metric 2
                    > to 192.168.0.2 via ge-0/0/1.0
224.0.0.5/32       *[OSPF/10] 00:21:41, metric 1
                      MultiRecv

(2) BGP基本設定

1. BGP Peer相手となる対向vSRXとの疎通性を確認しておく
まず、vSRX-1とvSRX-3の間で、ループバックインタフェース間での疎通性を確認しておきます。

root@vSRX-1> ping 10.0.0.3 source 10.0.0.1 count 5 
PING 10.0.0.3 (10.0.0.3): 56 data bytes
64 bytes from 10.0.0.3: icmp_seq=0 ttl=63 time=3.796 ms
64 bytes from 10.0.0.3: icmp_seq=1 ttl=63 time=1.610 ms
64 bytes from 10.0.0.3: icmp_seq=2 ttl=63 time=1.324 ms
64 bytes from 10.0.0.3: icmp_seq=3 ttl=63 time=1.154 ms
64 bytes from 10.0.0.3: icmp_seq=4 ttl=63 time=1.340 ms

--- 10.0.0.3 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 1.154/1.845/3.796/0.986 ms

問題なく、ping動作しております。

2. routing-optionを設定する
ここからは、ほぼ前回の記事と同じ作業になります。
BGP Peerアドレスとして、ループバックインタフェースを指定する点が異なります。

root@vSRX-1# set routing-options router-id 10.0.0.1 autonomous-system 65000    
root@vSRX-1# show | compare 
[edit]
+  routing-options {
+      router-id 10.0.0.1;
+      autonomous-system 65000;
+  }

root@vSRX-1# commit 

3. protocolsを設定する

root@vSRX-1# set protocols bgp group INTERNAL type internal local-address 10.0.0.1 neighbor 10.0.0.3
root@vSRX-1# show | compare 
[edit protocols]
+   bgp {
+       group INTERNAL {
+           type internal;
+           local-address 10.0.0.1;
+           neighbor 10.0.0.3;
+       }
+   }

root@vSRX-1# commit

4. BGP Peerの状態を確認しておく

root@vSRX-1> show bgp neighbor 
Peer: 10.0.0.3+57167 AS 65000  Local: 10.0.0.1+179 AS 65000
  Type: Internal    State: Established    Flags: <Sync>
  Last State: OpenConfirm   Last Event: RecvKeepAlive
  Last Error: None
  Options: <Preference LocalAddress Refresh>
  Local Address: 10.0.0.1 Holdtime: 90 Preference: 170
  Number of flaps: 0
  Peer ID: 10.0.0.3        Local ID: 10.0.0.1          Active Holdtime: 90
  Keepalive Interval: 30         Group index: 0    Peer index: 0   
  BFD: disabled, down
  NLRI for restart configured on peer: inet-unicast
  NLRI advertised by peer: inet-unicast
  NLRI for this session: inet-unicast
  Peer supports Refresh capability (2)
  Stale routes from peer are kept for: 300
  Peer does not support Restarter functionality
  NLRI that restart is negotiated for: inet-unicast
  NLRI of received end-of-rib markers: inet-unicast
  NLRI of all end-of-rib markers sent: inet-unicast
  Peer supports 4 byte AS extension (peer-as 65000)
  Peer does not support Addpath
  Table inet.0 Bit: 10000
    RIB State: BGP restart is complete
    Send state: in sync
    Active prefixes:              0
    Received prefixes:            0
    Accepted prefixes:            0
    Suppressed due to damping:    0
    Advertised prefixes:          0
  Last traffic (seconds): Received 21   Sent 21   Checked 21  
  Input messages:  Total 3      Updates 1       Refreshes 0     Octets 101
  Output messages: Total 3      Updates 0       Refreshes 0     Octets 120
  Output Queue[0]: 0            (inet.0, init-unicast)

5. Export policy適用
こちらも、前回と同様に、Export policy作成して、適用します。
自分が保持している経路を、BGP Peerに対して、アドバタイズするには、Export policyを作成して、BGPピアのGroupに適用する必要があるようです。

root@vSRX-1# set policy-options policy-statement export-bgp term 1 from route-filter 172.16.0.0/30 exact
root@vSRX-1# set policy-options policy-statement export-bgp term 1 then accept 
root@vSRX-1# set protocols bgp group INTERNAL export export-bgp 
root@vSRX-1# show | compare 
[edit protocols bgp group INTERNAL]
+    export export-bgp;
[edit]
+  policy-options {
+      policy-statement export-bgp {
+          term 1 {
+              from {
+                  route-filter 172.16.0.0/30 exact;
+              }
+              then accept;
+          }
+      }
+  }

root@vSRX-1# commit

すると、想定どおりに、自分が保持している経路をアドバタイズするようになりました。

root@vSRX-1> show route advertising-protocol bgp 10.0.0.3      

inet.0: 9 destinations, 9 routes (9 active, 0 holddown, 0 hidden)
  Prefix                  Nexthop              MED     Lclpref    AS path
* 172.16.0.0/30           Self                         100        I

さらに、対向側(vSRX-3ルータ)でも、Export policy適用してみると、アドバタイズされた経路を受信できるようになりました。

root@vSRX-1> show route receive-protocol bgp 10.0.0.3    

inet.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
  Prefix                  Nexthop              MED     Lclpref    AS path
* 172.16.1.0/30           10.0.0.3                     100        I

今回は、さらに、vSRX-3側でスタティック経路も追加しておきます。

root@vSRX-3# set routing-options static route 10.0.0.0/24 next-hop 172.16.1.2
root@vSRX-3# set routing-options static route 10.0.1.0/24 next-hop 172.16.1.2 
root@vSRX-3# set routing-options static route 10.0.2.0/24 next-hop 172.16.1.2
root@vSRX-3# show | compare 
[edit routing-options]
+   static {
+       route 10.0.0.0/24 next-hop 172.16.1.2;
+       route 10.0.1.0/24 next-hop 172.16.1.2;
+       route 10.0.2.0/24 next-hop 172.16.1.2;
+   }

root@vSRX-3# commit 

ただし、このままでは、vSRX-1側では、スタティック経路を受信しておりません。
vSRX-3側で保持しているスタティック経路を、BGP再配布するには、Export policyを作成して、BGPピアのGroupに適用する必要があるようです。

root@vSRX-1> show route receive-protocol bgp 10.0.0.3    

inet.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
  Prefix                  Nexthop              MED     Lclpref    AS path
* 172.16.1.0/30           10.0.0.3                     100        I

6. Export policy再適用
スタティック経路のBGP再配布に関わるExport policy作成して、適用します。

root@vSRX-3# set policy-options prefix-list PREFIX 10.0.0.0/24
root@vSRX-3# set policy-options prefix-list PREFIX 10.0.1.0/24
root@vSRX-3# set policy-options prefix-list PREFIX 10.0.2.0/24
root@vSRX-3# set policy-options policy-statement export-bgp term 1 from prefix-list PREFIX 
root@vSRX-3# set policy-options policy-statement export-bgp term 1 then accept
root@vSRX-3# show | compare 
[edit policy-options]
+   prefix-list PREFIX {
+       10.0.0.0/24;
+       10.0.1.0/24;
+       10.0.2.0/24;
+   }
[edit policy-options policy-statement export-bgp term 1 from]
+      prefix-list PREFIX;

root@vSRX-3# commit

この状態でのpolicy-options設定内容は、こんな感じになっています。

policy-options {
    prefix-list PREFIX {
        10.0.0.0/24;
        10.0.1.0/24;
        10.0.2.0/24;
    }
    policy-statement export-bgp {
        term 1 {                        
            from {
                route-filter 172.16.1.0/30 exact;
                prefix-list PREFIX;
            }
            then accept;
        }
    }
}

すると、vSRX-1ルータ側では、アドバタイズされたスタティック経路も受信できるようになりました。

root@vSRX-1> show route receive-protocol bgp 10.0.0.3    

inet.0: 13 destinations, 13 routes (13 active, 0 holddown, 0 hidden)
  Prefix                  Nexthop              MED     Lclpref    AS path
* 10.0.0.0/24             172.16.1.2                   100        I
* 10.0.1.0/24             172.16.1.2                   100        I
* 10.0.2.0/24             172.16.1.2                   100        I
* 172.16.1.0/30           10.0.0.3                     100        I

ここまでは、順調ですね。

(3) エンド端末間での疎通確認

それでは、OSPF, BGPの基本設定も完了したので、最後に、エンドエンド端末間でpingをうってみましょう。

tsubo@pc1:~$ ping 172.16.1.2
PING 172.16.1.2 (172.16.1.2) 56(84) bytes of data.
^C
--- 172.16.1.2 ping statistics ---
15 packets transmitted, 0 received, 100% packet loss, time 13999ms

残念ながら、pingが成功しません!
ここでの課題は、vSRX-2では、"172.16.1.0/30"のルーティング情報を保持しておりません。
よって、ICMP Echo Requestのパケットを受信しても、vSRX-3に転送せずに、ドロップしてしまった!
こちらのSlideShareが参考になると思います。

www.slideshare.net

(4) MPLS / LDP基本設定

早速、先ほどの課題解決のために、MPLS / LDPを設定します。
1. interfacesを設定する

root@vSRX-1# set interfaces ge-0/0/1 unit 0 family mpls 
root@vSRX-1# show | compare 
[edit interfaces ge-0/0/1 unit 0]
+      family mpls;

root@vSRX-1# commit

2. protocolsを設定する
MPLSも有効にします。

root@vSRX-1# set protocols mpls interface ge-0/0/1.0    
root@vSRX-1# show | compare 
[edit protocols]
+   mpls {
+       interface ge-0/0/1.0;
+   }

root@vSRX-1# commit 

LDPも有効にします。

root@vSRX-1# set protocols ldp interface ge-0/0/1.0 

[edit]
root@vSRX-1# show | compare 
[edit protocols]
+   ldp {
+       interface ge-0/0/1.0;
+   }

[edit]
root@vSRX-1# commit 
commit complete


3. LDP隣接関係の状態を確認しておく
すべてのvSRXルータでも同様に、MPLS / LDPを設定します。
そして、vSRX-1で、LDP隣接状態を確認してみます。

root@vSRX-1> show ldp neighbor 
Address            Interface          Label space ID         Hold time
192.168.0.2        ge-0/0/1.0         10.0.0.2:0               12

root@vSRX-1> show ldp interface   
Interface            Label space ID        Nbr count   Next hello
ge-0/0/1.0           10.0.0.1:0               1           4

さらに、vSRX-2でも、LDP隣接状態を確認してみます。

root@vSRX-2> show ldp neighbor     
Address            Interface          Label space ID         Hold time
192.168.0.1        ge-0/0/0.0         10.0.0.1:0               12
192.168.1.2        ge-0/0/1.0         10.0.0.3:0               14

root@vSRX-2> show ldp interface    
Interface            Label space ID        Nbr count   Next hello
ge-0/0/0.0           10.0.0.2:0               1           1
ge-0/0/1.0           10.0.0.2:0               1           2

vSRX-3でも、OSPF隣接状態を確認しておきます。

root@vSRX-3> show ldp neighbor     
Address            Interface          Label space ID         Hold time
192.168.1.1        ge-0/0/0.0         10.0.0.2:0               10

root@vSRX-3> show ldp interface    
Interface            Label space ID        Nbr count   Next hello
ge-0/0/0.0           10.0.0.3:0               1           3

LDPプロトコルによって学習したルーティング情報を確認しておきます。

root@vSRX-1> show route protocol ldp             

inet.0: 13 destinations, 13 routes (13 active, 0 holddown, 0 hidden)

inet.3: 2 destinations, 2 routes (2 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

10.0.0.2/32        *[LDP/9] 00:14:15, metric 1
                    > to 192.168.0.2 via ge-0/0/1.0
10.0.0.3/32        *[LDP/9] 00:13:31, metric 1
                    > to 192.168.0.2 via ge-0/0/1.0, Push 299792

mpls.0: 7 destinations, 7 routes (7 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

299776             *[LDP/9] 00:14:15, metric 1
                    > to 192.168.0.2 via ge-0/0/1.0, Pop      
299776(S=0)        *[LDP/9] 00:14:15, metric 1
                    > to 192.168.0.2 via ge-0/0/1.0, Pop      
299792             *[LDP/9] 00:13:31, metric 1
                    > to 192.168.0.2 via ge-0/0/1.0, Swap 299792

(5) エンド端末間での疎通確認

それでは、LDPの基本設定も完了したので、もう一度、エンドエンド端末間でpingをうってみましょう。

tsubo@pc1:~$ ping 172.16.1.2
PING 172.16.1.2 (172.16.1.2) 56(84) bytes of data.
64 bytes from 172.16.1.2: icmp_seq=1 ttl=61 time=0.758 ms
64 bytes from 172.16.1.2: icmp_seq=2 ttl=61 time=0.728 ms
64 bytes from 172.16.1.2: icmp_seq=3 ttl=61 time=0.758 ms
64 bytes from 172.16.1.2: icmp_seq=4 ttl=61 time=0.828 ms
64 bytes from 172.16.1.2: icmp_seq=5 ttl=61 time=55.9 ms
^C
--- 172.16.1.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3999ms
rtt min/avg/max/mdev = 0.728/11.812/55.992/22.090 ms||<

今回は、pingが成功しましたね!

この時、pc2側でtcpdumpを動作させて、パケットモニタリングしておきます。

root@pc2:~# tcpdump -i eth1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
17:29:11.583949 IP 172.16.0.1 > 172.16.1.2: ICMP echo request, id 2198, seq 1, length 64
17:29:11.583983 IP 172.16.1.2 > 172.16.0.1: ICMP echo reply, id 2198, seq 1, length 64
17:29:12.586912 IP 172.16.0.1 > 172.16.1.2: ICMP echo request, id 2198, seq 2, length 64
17:29:12.586940 IP 172.16.1.2 > 172.16.0.1: ICMP echo reply, id 2198, seq 2, length 64
17:29:13.588175 IP 172.16.0.1 > 172.16.1.2: ICMP echo request, id 2198, seq 3, length 64
17:29:13.588204 IP 172.16.1.2 > 172.16.0.1: ICMP echo reply, id 2198, seq 3, length 64
17:29:14.588207 IP 172.16.0.1 > 172.16.1.2: ICMP echo request, id 2198, seq 4, length 64
17:29:14.588240 IP 172.16.1.2 > 172.16.0.1: ICMP echo reply, id 2198, seq 4, length 64
17:29:14.974839 IP 192.168.105.1.17500 > 192.168.105.255.17500: UDP, length 131
17:29:15.590265 IP 172.16.0.1 > 172.16.1.2: ICMP echo request, id 2198, seq 5, length 64
17:29:15.590293 IP 172.16.1.2 > 172.16.0.1: ICMP echo reply, id 2198, seq 5, length 64

こちらも期待通り、ICMP Echo Request / Replyが観測できました。

さらに、pc2側の"10.0.0.1"に対して、pingをうってみましょう。

tsubo@pc1:~$ ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.273 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.353 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.328 ms
64 bytes from 10.0.0.1: icmp_seq=4 ttl=64 time=0.312 ms
64 bytes from 10.0.0.1: icmp_seq=5 ttl=64 time=0.264 ms
^C
--- 10.0.0.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3999ms
rtt min/avg/max/mdev = 0.264/0.306/0.353/0.033 ms

こちらも、pingは、成功しました。

この時、pc2側でtcpdumpを動作させて、パケットモニタリングしておきます。

root@pc2:~# tcpdump -i eth1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel

あれっ?
こちらは、期待に反して、ICMP Echo Request / Replyが観測できません。

pc2には、ICMP Echo Requestパケットが届いていないのに、pc1では、ICMP Echo Replyを受信してしまっている。
という、奇妙に現象が発生してしまいました。

◼︎ 最終的なコンフィグ内容の確認

root@vSRX-1> show configuration | display set 
set version 15.1X49-D15.4
set system host-name vSRX-1
set system time-zone Asia/Tokyo
set system root-authentication encrypted-password "$1$XvI2vVPR$7lnJdb5gNeg4zql8lZ/1Q1"
set system login user tsubo uid 2000
set system login user tsubo class super-user
set system login user tsubo authentication encrypted-password "$1$THS.KlMg$SvjUk8SUxyDY3rDRGT3hK/"
set system services ssh root-login allow
set system services web-management http interface fxp0.0
set system syslog user * any emergency
set system syslog file messages any any
set system syslog file messages authorization info
set system syslog file interactive-commands interactive-commands any
set system license autoupdate url https://ae1.juniper.net/junos/key_retrieval
set security forwarding-options family inet6 mode packet-based
set security forwarding-options family mpls mode packet-based
set interfaces ge-0/0/0 unit 0 family inet address 172.16.0.2/30
set interfaces ge-0/0/1 unit 0 family inet address 192.168.0.1/30
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces fxp0 unit 0
set interfaces lo0 unit 0 family inet address 10.0.0.1/32
set routing-options router-id 10.0.0.1
set routing-options autonomous-system 65000
set protocols mpls interface ge-0/0/1.0
set protocols bgp group INTERNAL type internal
set protocols bgp group INTERNAL local-address 10.0.0.1
set protocols bgp group INTERNAL export export-bgp
set protocols bgp group INTERNAL neighbor 10.0.0.3
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0 passive
set protocols ldp interface ge-0/0/1.0
set policy-options policy-statement export-bgp term 1 from route-filter 172.16.0.0/30 exact
set policy-options policy-statement export-bgp term 1 then accept

◼︎ 最後に

先ほどの課題は、次回、考察したいと思います。
以上です。

vSRXを活用した、JUNOSのお勉強まとめ【JUNOS初期設定 + BGP基本編】

これから、しばらく、ネットワーク技術のお勉強に時間を費やしたいと思います。
ネットワークのお勉強の素材としては、「Junos設定&管理完全Bible」を選びました。
ただ、こちらの書籍なんですが、すでに、在庫が存在しておらず、中古品をゲットすることになりました。

gihyo.jp

◼︎ vSRX環境構築

以前のブログ記事「Mac OS X環境のVMware Fusion上で、vSRXを動かす」をそのまま活用します。
本来、vSRXは、Flowベースの仮想ファイアウォールなのですが、ルーティングの勉強用には、Packetベースに動作モードを変更しております。ご注意ください。
ttsubo.hatenablog.com

◼︎ vSRXトポロジ構成

Mac OS X環境で、複数台のvSRXを動作させたいところですが、今回は、vSRX2台構成としました。

                   +---------+                i-BGP                +--------+
... -------------+ | vSRX-1  | +---------------------------------+ | vSRX-2 | +---- ...
     172.16.0.1/24 |         | 192.168.0.1/30       192.168.0.2/30 |        | 172.16.1.1/24
                   +---------+                                     +--------+
                   < AS65000 >                                     < AS65000 >

◼︎ JUNOS基本設定

ネットワークエンジニア界隈で、よく使用されているJUNOS操作の習熟を目指して、最初から環境設定に取り掛かります。

(1) vSRXの初期設定

1. rootパスワードを設定する
初期状態では、rootユーザのパスワードは設定されていないので、パスワードを設定する

root# set system root-authentication plain-text-password 
New password:
Retype new password:

2. ホスト名を設定する

root# set system host-name vSRX-1
root# commit

3. タイムゾーンを設定する

root@vSRX-1# set system time-zone Asia/Tokyo 
root@vSRX-1# commit

4. 一般ユーザを作成する

root@vSRX-1# set system login user tsubo class super-user 
root@vSRX-1# set system login user tsubo authentication plain-text-password 
New password:
Retype new password:

5. vSRXの動作モードを変更する
vSRXは、通常、Flow-based forwardingモードで動作しているので、packet-based forwardingモードに変更しておきます。
動作モードの変更を有効にするために、vSRXを再起動します。

root@vSRX-1# delete security 
root@vSRX-1# set security forwarding-options family mpls mode packet-based    
root@vSRX-1# set security forwarding-options family inet6 mode packet-based
root@vSRX-1# commit
root@vSRX-1# exit 
Exiting configuration mode

root@vSRX-1> request system reboot
Reboot the system ? [yes,no] (no) yes 

Shutdown NOW!

すると、packet basedに変更されたことが確認できるようになります。

root@vSRX-1> show security flow status 
  Flow forwarding mode:
    Inet forwarding mode: packet based
    Inet6 forwarding mode: packet based
    MPLS forwarding mode: packet based
    ISO forwarding mode: drop
  Flow trace status
    Flow tracing status: off
  Flow session distribution
    Distribution mode: RR-based
  Flow ipsec performance acceleration: off
  Flow packet ordering
    Ordering mode: Hardware

6. rootユーザでのsshログインを許可する

root@vSRX-1# set system services ssh root-login allow

7. ge-0/0/0.0にアドレスを付与する

root@vSRX-1# set interfaces ge-0/0/0 unit 0 family inet address 192.168.0.1/24
root@vSRX-1# commit

root@vSRX-1# run show interfaces ge-0/0/0.0 brief 
  Logical interface ge-0/0/0.0 
    Flags: Up SNMP-Traps 0x4000 Encapsulation: ENET2
    Security: Zone: Null
    inet  192.168.0.1/24  

8. ループバックを設定する

root@vSRX-1# set interfaces lo0 unit 0 family inet address 10.0.0.1 
root@vSRX-1# commit

root@vSRX-1# run show interfaces lo0.0 brief  
  Logical interface lo0.0 
    Flags: Down SNMP-Traps Encapsulation: Unspecified
    Security: Zone: Null
    inet  10.0.0.1         --> 0/0

(2) BGP基本設定

1. 対向vSRXとの疎通性を確認しておく

root@vSRX-1# run ping 192.168.0.2 count 5   
PING 192.168.0.2 (192.168.0.2): 56 data bytes
64 bytes from 192.168.0.2: icmp_seq=0 ttl=64 time=3.993 ms
64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=1.605 ms
64 bytes from 192.168.0.2: icmp_seq=2 ttl=64 time=1.101 ms
64 bytes from 192.168.0.2: icmp_seq=3 ttl=64 time=3.063 ms
64 bytes from 192.168.0.2: icmp_seq=4 ttl=64 time=1.969 ms

--- 192.168.0.2 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 1.101/2.346/3.993/1.046 ms

2. routing-optionを設定する

root@vSRX-1# set routing-options router-id 10.0.0.1 autonomous-system 65000 
root@vSRX-1# show | compare 
[edit]
+  routing-options {
+      router-id 10.0.0.1;
+      autonomous-system 65000;
+  }

root@vSRX-1# commit

3. protocolsを設定する

root@vSRX-1# set protocols bgp group INTERNAL type internal neighbor 192.168.0.2
root@vSRX-1# show | compare                                                 
[edit]
+  protocols {
+      bgp {
+          group INTERNAL {
+              type internal;
+              neighbor 192.168.0.2;
+          }
+      }
+  }

root@vSRX-1# commit  

4. BGP Peerの状態を確認しておく

root@vSRX-1> show bgp neighbor    
Peer: 192.168.0.2+55175 AS 65000 Local: 192.168.0.1+179 AS 65000
  Type: Internal    State: Established    Flags: <Sync>
  Last State: OpenConfirm   Last Event: RecvKeepAlive
  Last Error: Open Message Error
  Options: <Preference Refresh>
  Holdtime: 90 Preference: 170
  Number of flaps: 0
  Error: 'Open Message Error' Sent: 3 Recv: 0
  Peer ID: 10.0.0.2        Local ID: 10.0.0.1          Active Holdtime: 90
  Keepalive Interval: 30         Group index: 0    Peer index: 0   
  BFD: disabled, down
  NLRI for restart configured on peer: inet-unicast
  NLRI advertised by peer: inet-unicast
  NLRI for this session: inet-unicast
  Peer supports Refresh capability (2)
  Stale routes from peer are kept for: 300
  Peer does not support Restarter functionality
  NLRI that restart is negotiated for: inet-unicast
  NLRI of received end-of-rib markers: inet-unicast
  NLRI of all end-of-rib markers sent: inet-unicast
  Peer supports 4 byte AS extension (peer-as 65000)
  Peer does not support Addpath
  Table inet.0 Bit: 10000
    RIB State: BGP restart is complete
    Send state: in sync
    Active prefixes:              0
    Received prefixes:            0
    Accepted prefixes:            0
    Suppressed due to damping:    0
    Advertised prefixes:          0
  Last traffic (seconds): Received 3    Sent 3    Checked 83  
  Input messages:  Total 6      Updates 1       Refreshes 0     Octets 158
  Output messages: Total 6      Updates 0       Refreshes 0     Octets 177
  Output Queue[0]: 0            (inet.0, inet-unicast)

(3) down-link側の設定

1. ge-0/0/1.0にアドレスを付与する

root@vSRX-1# set interfaces ge-0/0/1 unit 0 family inet address 172.16.0.1/24 
root@vSRX-1# show | compare                                                      
[edit interfaces]
+   ge-0/0/1 {
+       unit 0 {
+           family inet {
+               address 172.16.0.1/24;
+           }
+       }
+   }

root@vSRX-1# commit 


2. Export policy作成と適用
自分が保持している経路を、BGP Peerに対して、アドバタイズするには、Export policyを作成して、BGPピアのGroupに適用する必要があるようです。

root@vSRX-1# set policy-options policy-statement export-bgp term 1 from route-filter 172.16.0.0/24 exact    
root@vSRX-1# set policy-options policy-statement export-bgp term 1 then accept 
root@vSRX-1# set protocols bgp group INTERNAL export export-bgp  
root@vSRX-1# show | compare 
[edit protocols bgp group INTERNAL]
+    export export-bgp;
[edit]
+  policy-options {
+      policy-statement export-bgp {
+          term 1 {
+              from {
+                  route-filter 172.16.0.0/24 exact;
+              }
+              then accept;
+          }
+      }
+  }

root@vSRX-1# commit 

すると、自分が保持している経路をアドバタイズするようになりました。

root@vSRX-1> show route advertising-protocol bgp 192.168.0.2 

inet.0: 6 destinations, 6 routes (6 active, 0 holddown, 0 hidden)
  Prefix                  Nexthop              MED     Lclpref    AS path
* 172.16.0.0/24           Self                         100        I

さらに、対向側でもExport policy作成と適用してみると、アドバタイズされた経路を受信できるようになりました。

root@vSRX-1> show route receive-protocol bgp 192.168.0.2 

inet.0: 6 destinations, 6 routes (6 active, 0 holddown, 0 hidden)
  Prefix                  Nexthop              MED     Lclpref    AS path
* 172.16.1.0/24           192.168.0.2                  100        I

(4) エンド端末間での疎通確認

tsubo@pc1:~$ ping 172.16.1.2
PING 172.16.1.2 (172.16.1.2) 56(84) bytes of data.
64 bytes from 172.16.1.2: icmp_seq=1 ttl=62 time=83.3 ms
64 bytes from 172.16.1.2: icmp_seq=2 ttl=62 time=0.584 ms
64 bytes from 172.16.1.2: icmp_seq=3 ttl=62 time=0.701 ms
64 bytes from 172.16.1.2: icmp_seq=4 ttl=62 time=0.634 ms
64 bytes from 172.16.1.2: icmp_seq=5 ttl=62 time=0.678 ms
^C
--- 172.16.1.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4001ms
rtt min/avg/max/mdev = 0.584/17.194/83.377/33.091 ms

◼︎ 最終的なコンフィグ内容の確認

root@vSRX-1> show configuration | display set 
set version 15.1X49-D15.4
set system host-name vSRX-1
set system time-zone Asia/Tokyo
set system root-authentication encrypted-password "$1$XvI2vVPR$7lnJdb5gNeg4zql8lZ/1Q1"
set system login user tsubo uid 2000
set system login user tsubo class super-user
set system login user tsubo authentication encrypted-password "$1$THS.KlMg$SvjUk8SUxyDY3rDRGT3hK/"
set system services ssh root-login allow
set system services web-management http interface fxp0.0
set system syslog user * any emergency
set system syslog file messages any any
set system syslog file messages authorization info
set system syslog file interactive-commands interactive-commands any
set system license autoupdate url https://ae1.juniper.net/junos/key_retrieval
set security forwarding-options family inet6 mode packet-based
set security forwarding-options family mpls mode packet-based
set interfaces ge-0/0/0 unit 0 family inet address 192.168.0.1/24
set interfaces ge-0/0/1 unit 0 family inet address 172.16.0.1/24
set interfaces fxp0 unit 0
set interfaces lo0 unit 0 family inet address 10.0.0.1/32
set routing-options router-id 10.0.0.1
set routing-options autonomous-system 65000
set protocols bgp group INTERNAL type internal
set protocols bgp group INTERNAL export export-bgp
set protocols bgp group INTERNAL neighbor 192.168.0.2
set policy-options policy-statement export-bgp term 1 from route-filter 172.16.0.0/24 exact
set policy-options policy-statement export-bgp term 1 then accept

◼︎ 最後に

まぁ、今回は、単に、BGPを動かしただけなので、これといったコメントはありません。
以上です。

etcdの分散Key-Valuesストアを試してみる

CoreOSが提供するetcdの動作をお手軽に試してみました。
なお、etcdとは、分散Key-Valuesストアを使い,各種設定をノード間で共有するメカニズムだそうです。

etcd is a distributed key value store that provides a reliable way to store data across a cluster of machines.
It’s open-source and available on GitHub. etcd gracefully handles master elections during network partitions
and will tolerate machine failure, including the master.

f:id:ttsubo:20151227155040p:plain

◼️ まずは、環境準備から ...

まずは、golang環境を準備しておきます。
Getting Started - The Go Programming Language

そして、Mac OS X環境で、etcdを動作させる場合、こちらのGetting Startedに従って作業します。
github.com

$ curl -L  https://github.com/coreos/etcd/releases/download/v2.2.2/etcd-v2.2.2-darwin-amd64.zip -o etcd-v2.2.2-darwin-amd64.zip
$ unzip etcd-v2.2.2-darwin-amd64.zip
$ cd etcd-v2.2.2-darwin-amd64
$ ./etcd
2015-12-27 15:42:15.142251 I | etcdmain: etcd Version: 2.2.2
2015-12-27 15:42:15.142330 I | etcdmain: Git SHA: b4bddf6
2015-12-27 15:42:15.142346 I | etcdmain: Go Version: go1.5.1
2015-12-27 15:42:15.142349 I | etcdmain: Go OS/Arch: darwin/amd64
2015-12-27 15:42:15.142360 I | etcdmain: setting maximum number of CPUs to 8, total number of available CPUs is 8
2015-12-27 15:42:15.142364 W | etcdmain: no data-dir provided, using default data-dir ./default.etcd
2015-12-27 15:42:15.142982 I | etcdmain: listening for peers on http://localhost:2380
2015-12-27 15:42:15.143166 I | etcdmain: listening for peers on http://localhost:7001
2015-12-27 15:42:15.143335 I | etcdmain: listening for client requests on http://localhost:2379
2015-12-27 15:42:15.143499 I | etcdmain: listening for client requests on http://localhost:4001
2015-12-27 15:42:15.144404 I | etcdserver: name = default
2015-12-27 15:42:15.144433 I | etcdserver: data dir = default.etcd
2015-12-27 15:42:15.144447 I | etcdserver: member dir = default.etcd/member
2015-12-27 15:42:15.144451 I | etcdserver: heartbeat = 100ms
2015-12-27 15:42:15.144453 I | etcdserver: election = 1000ms
2015-12-27 15:42:15.144467 I | etcdserver: snapshot count = 10000
2015-12-27 15:42:15.144474 I | etcdserver: advertise client URLs = http://localhost:2379,http://localhost:4001
2015-12-27 15:42:15.144478 I | etcdserver: initial advertise peer URLs = http://localhost:2380,http://localhost:7001
2015-12-27 15:42:15.144487 I | etcdserver: initial cluster = default=http://localhost:2380,default=http://localhost:7001
2015-12-27 15:42:15.145942 I | etcdserver: starting member ce2a822cea30bfca in cluster 7e27652122e8b2ae
2015-12-27 15:42:15.146367 I | raft: ce2a822cea30bfca became follower at term 0
2015-12-27 15:42:15.146404 I | raft: newRaft ce2a822cea30bfca [peers: [], term: 0, commit: 0, applied: 0, lastindex: 0, lastterm: 0]
2015-12-27 15:42:15.146415 I | raft: ce2a822cea30bfca became follower at term 1
2015-12-27 15:42:15.146776 I | etcdserver: starting server... [version: 2.2.2, cluster version: to_be_decided]
2015-12-27 15:42:15.147874 N | etcdserver: added local member ce2a822cea30bfca [http://localhost:2380 http://localhost:7001] to cluster 7e27652122e8b2ae
2015-12-27 15:42:15.147934 E | etcdserver: cannot monitor file descriptor usage (cannot get FDUsage on darwin)
2015-12-27 15:42:15.548221 I | raft: ce2a822cea30bfca is starting a new election at term 1
2015-12-27 15:42:15.548284 I | raft: ce2a822cea30bfca became candidate at term 2
2015-12-27 15:42:15.548299 I | raft: ce2a822cea30bfca received vote from ce2a822cea30bfca at term 2
2015-12-27 15:42:15.548316 I | raft: ce2a822cea30bfca became leader at term 2
2015-12-27 15:42:15.548330 I | raft: raft.node: ce2a822cea30bfca elected leader ce2a822cea30bfca at term 2
2015-12-27 15:42:15.548736 I | etcdserver: published {Name:default ClientURLs:[http://localhost:2379 http://localhost:4001]} to cluster 7e27652122e8b2ae
2015-12-27 15:42:15.548760 I | etcdserver: setting up the initial cluster version to 2.2
2015-12-27 15:42:15.550378 N | etcdserver: set the initial cluster version to 2.2

◼️ オープンソース"go-etcd"を活用して、etcdの分散Key-Valuesストアを試してみる

それでは、オープンソース"go-etcd"を活用して、golangから、etcdを試してみます。

github.com

0. etcdのKey-Valuesストアを確認する
別ターミナルから、etcdctlコマンドで、etcdのストアデータを確認してみます。
まだ、etcdを起動したばかりなので、何も表示されないですけど。

$ ./etcdctl --peers "http://127.0.0.1:4001" ls

1. etcdにサンプルデータをセットしてみる
オープンソース"go-etcd"を活用して、手軽にKey-Valuesストアをセットできるサンプルコードを準備しておきます。

package main

import (
    "github.com/coreos/go-etcd/etcd"
    "log"
)

var etcdServers = []string{
    "http://127.0.0.1:4001",
}

func SetStatus_for_server(client *etcd.Client, status string){
    // Set "active" to path of "/state/server"
    _, err := client.Set("/state/server", status, 0)
    if err != nil {
        log.Fatal(err)
    }
}

func SetStatus_for_network(client *etcd.Client, status string){
    // Set "active" to path of "/state/network"
    _, err := client.Set("/state/network", status, 0)
    if err != nil {
        log.Fatal(err)
    }
}

func main() {
    // Create etcd client
    client := etcd.NewClient(etcdServers)
    SetStatus_for_server(client, "active")
    SetStatus_for_network(client, "down")
}

サンプルコードを実行して、etcdにサンプルデータをセットしてみます。
エラーが表示されなければOKです。

$ go run go-etcd-set.go

2. etcdctlコマンドで、再度、Key-Valuesストアを確認する
今度は、"/state"という結果が表示されました。

$ ./etcdctl --peers "http://127.0.0.1:4001" ls
/state

ちなみに、etcdのKey-Valuesストアデータは、ディレクトリ構造(=パス)で管理されております。
/state配下のパスを確認する場合は、"--recursive"を指定します。

$ ./etcdctl --peers "http://127.0.0.1:4001" ls --recursive
/state
/state/server
/state/network

続いて、Key-ValuesストアのValuesを確認してみます。
まずは、"server"が"active"の状態であることを確認します。

$ ./etcdctl --peers "http://127.0.0.1:4001" get /state/server
active

つぎに、"network"が"down"の状態であることを確認します。

$ ./etcdctl --peers "http://127.0.0.1:4001" get /state/network
down

3. もっと、お手軽にKey-ValuesストアのValuesを確認してみる
オープンソース"go-etcd"を活用して、手軽にKey-Valuesストアを確認できるサンプルコードを準備しておきます。

package main

import (
    "github.com/coreos/go-etcd/etcd"
    "log"
    "fmt"
)

var etcdServers = []string{
    "http://127.0.0.1:4001",
}

func main() {
    client := etcd.NewClient(etcdServers)
    resp, err := client.Get("/state", false, true)
    if err != nil {
        log.Fatal(err)
    }
    for _, n := range resp.Node.Nodes {
        fmt.Printf("%s: %s\n", n.Key, n.Value)
    }
}

サンプルコードを実行して、Key-ValuesストアのValuesを確認してみます。

$ go run go-etcd-get.go 
/state/server: active
/state/network: down

4. etcdのKey-ValuesストアのValuesを監視してみる
ここからは、応用編です。
Key-ValuesストアのValuesが外部要因によって変更された場合、そのValuesをモニタ監視するサンプルコードを準備しておきます。
12/28追記:パス情報が複数変更された場合でも、モニタ監視できるようサンプルコードを修正しました。

package main

import (
    "os"
    "os/signal"
    "github.com/coreos/go-etcd/etcd"
    "log"
)

var etcdServers = []string{
    "http://127.0.0.1:4001",
}

func main() {
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    etcdResponseChan := make(chan *etcd.Response)
    client := etcd.NewClient(etcdServers)
    go func(){
        _, err := client.Watch("/state", 0, true, etcdResponseChan, nil)
        if err != nil {
            log.Fatal(err)
        }
    }()
    log.Println("Waiting for an update...")

    go func(){
        for {
            response := <-etcdResponseChan
            log.Printf("Got updated state : path=[%s], value=[%s]\n",
                        response.Node.Key, response.Node.Value)
        }
    }()
    <-c
}

サンプルコードを実行して、Valuesをモニタ監視できるよう準備しておきます。

$ go run ./go-etcd-watch.go 
2015/12/27 16:46:16 Waiting for an update...

続いて、別ターミナルから、"network"を"active"の状態に更新します。
更新方法は、なんでも良いのですが、curlコマンドで更新してみます。

$ curl -L http://127.0.0.1:4001/v2/keys/state/network -XPUT -d value='active'
{"action":"set","node":{"key":"/state/network","value":"active","modifiedIndex":6,"createdIndex":6},"prevNode":{"key":"/state/network","value":"down","modifiedIndex":5,"createdIndex":5}}

すると、先ほどモニタ監視として実行しておいたサンプルコードの起動画面にて、"network"が"active"の状態に更新されたことが検出できました。

$ go run ./go-etcd-watch.go 
2015/12/27 16:46:16 Waiting for an update...
2015/12/27 16:47:07 Got updated state : path=[/state/network], value=[active]

事後確認として、Key-ValuesストアのValuesを確認してみます。

$ go run go-etcd-get.go 
/state/server: active
/state/network: active

◼️ 終わりに

以上で、オープンソース"go-etcd"を活用による、etcdでの分散Key-Valuesストアの動作を試してみました。
etcdの分散Key-Valuesストア活用によるクラウド基盤において、いろいろな用途に応用できそうな手応えを感じることができました。

SDNアプローチによるBGP経路監視を試してみる

「ネットワーク運用の自動化」として、BGP経路監視を考察してみたいと思います。
NetOpsCoding Advent Calendar 2015 12/12分のエントリーです
qiita.com

◾️ ”BGP運用”という言葉から想定されるイメージ

BGP運用というと、なんとなく「BGP職人さん」という言葉を連想してしまうのは、私だけでしょうか?
BGP職人さんは、事前に策定されたBGP運用ポリシーに従って、ポリシー・ルーティングに関わるルートマップ等コンフィグを投入することができる存在であり、想定どおり、ポリシー・ルーティングが動作されているかを確認するため、ベンダ固有のCLIコマンドを駆使して細かなBGP制御を確認することを日々の業務として実施する。
そんなイメージを、「BGP運用」からは、連想してしまいます。

さらに、不測な事態としてNWトポロジが安定動作していないことが判明した際には、BGP等プロトコル動作の振る舞いから、障害箇所を推測して、長年の運用経験から導き出された障害対策案の中からベストな方法で障害復旧を施すことができるネットワーク達人こそが、大規模ネットワーク運用を担っていることでしょう。

◾️ ”BGP職人の世界”に運用自動化のメスを入れるためには?

BGP職人の運用経験を踏まえた、運用の自動化を実現するには、

  • 課題1「BGPコンフィグ設定通りに、BGP動作していることを、検証する仕組み」
  • 課題2「実ネットワークでのBGP動作履歴を、オペレーション観点で、ログ管理できる仕組み」
  • 課題3「BGP障害解析として、過去のBGP障害履歴から障害箇所を推定する仕組み」

が備わっているBGPシミュレーション機能を有する運用システムが必要になるのでしょう。

まずは、課題1「BGPコンフィグ設定通りに、BGP動作していることを、検証する仕組み」の解決方法と一つとして、End-End開通監視に着目した過去のSlideshareを共有しておきます。

www.slideshare.net

ここでのポイントは、従来のCLI, SNMPでのBGP監視では、リアルタイムな監視の実現が困難なので、BMPに着目してEnd-End開通監視のプロトタイプを実現しましたというネタになります。

◾️ SDNアプローチによるBGP経路監視を試してみる

前置きが長くなりましたが、ここからが本題です。

BGP経路監視ツール"bgpSimulator"の作成して、BGP経路監視の有効性を確認しました。
ちなみに、BGP経路監視には、BGPルータ側でのBMP機能が動作することを前提としております。
そこで、CSR1000v(IOS XE)を検証構成に配備しました。

(1) BGP経路監視ツール"bgpSimulator"

BGP経路監視ツール"bgpSimulator"は、”BMP処理部"と"BGPパス計算部"から構成されております。
前者は、Ryu BMP Serverを活用した実装コードになっております。また、後者は、Ryu BGPコードを改造して実現しております。
f:id:ttsubo:20151212055804j:plain
なお、動作としては、以下のような処理の流れによって実現しております。
1. まず、BGP経路監視ツール"bgpSimulator"の起動時に、CSR1000vのBGP構成をシミュレーション面として構築する
2. CSR1000vで、adj-RIB-inが変化したタイミングで、BGP UpdateメッセージをBMP情報として通知する
3. bgpSimulatorは、受信したBMP情報からBGP Updateメッセージを抽出する
4. bgpSimulatorは、"BGP1"のBGPシミュレーション面のadj-RIB-inに、BGP Ipdateメッセージを注入する
5. bgpSimulatorは、BGPシミュレーション面のadj-RIB-inの更新を契機に、BGPパス計算を行い、RIB-localに保持する
6. オペレータは、bgpSimulatorのコマンド上から、RIB-local情報を参照する

(2) BGP検証環境

全体のNWトポロジは、こんな感じです。CSR1000vは、BGP1の箇所に配備しております。

f:id:ttsubo:20151206183337j:plain

ちなみに、bgpSimulatorツールを起動した後に、"BGP6"ルータで、プレフィックス"172.16.0.0/24"を追加した上で、BGP経路監視の様子を確認しております。

(3) 動作結果の確認

まずは、bgpSimulatorツール起動後に、BMP情報を受信した様子を確認しておきます。

$ sudo ryu-manager bgpSimulator.py
loading app bgpSimulator.py
creating context wsgi
instantiating app None of SimpleBGPSpeaker
creating context bgps
instantiating app bgpSimulator.py of BgpSimulator
(3684) wsgi starting up on http://0.0.0.0:8080/
(3684) accepted ('127.0.0.1', 50301)

... (snip)

BMP client connected, ip=192.168.100.1, port=14855
Start BMP session!! [192.168.100.1]
Received BMPPeerUpNotification, peer=[10.0.0.1]
Received BMPRouteMonitoring=[{'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_RouteRefresh'}], peer=[10.0.0.1]
Received BMPRouteMonitoring=[{'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_RouteRefresh'}], peer=[10.0.0.1]
Received BMPPeerUpNotification, peer=[10.0.1.2]
Received BMPRouteMonitoring=[{'path_attributes': {'ORIGIN': '?', 'EXTENDED_COMMUNITIES': ['65010:101'], 'AS_PATH': [[65010]], 'MP_REACH_NLRI': {'nexthop': '192.168.101.101', 'nlri': [{'prefix': '192.168.2.0/24', 'label_list': [20], 'route_dist': '65010:101'}, {'prefix': '172.16.0.0/24', 'label_list': [22], 'route_dist': '65010:101'}]}}, 'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_Update'}], peer=[10.0.0.1]
Received BMPRouteMonitoring=[{'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_RouteRefresh'}], peer=[10.0.0.1]
Received BMPRouteMonitoring=[{'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_RouteRefresh'}], peer=[10.0.0.1]
Received BMPRouteMonitoring=[{'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_RouteRefresh'}], peer=[10.0.1.2]
Received BMPRouteMonitoring=[{'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_RouteRefresh'}], peer=[10.0.1.2]
Received BMPPeerUpNotification, peer=[10.0.1.3]
Received BMPRouteMonitoring=[{'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_RouteRefresh'}], peer=[10.0.1.2]
Received BMPRouteMonitoring=[{'path_attributes': {'ORIGIN': '?', 'MP_REACH_NLRI': {'nexthop': '192.168.104.102', 'nlri': [{'prefix': '192.168.2.0/24', 'label_list': [20], 'route_dist': '65010:101'}, {'prefix': '172.16.0.0/24', 'label_list': [22], 'route_dist': '65010:101'}]}, 'AS_PATH': [[65010]], 'MULTI_EXIT_DISC': 0, 'LOCAL_PREF': 100, 'EXTENDED_COMMUNITIES': ['65010:101']}, 'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_Update'}], peer=[10.0.1.2]
Received BMPRouteMonitoring=[{'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_RouteRefresh'}], peer=[10.0.1.2]
Received BMPRouteMonitoring=[{'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_RouteRefresh'}], peer=[10.0.1.3]
Received BMPRouteMonitoring=[{'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_RouteRefresh'}], peer=[10.0.1.3]
Received BMPRouteMonitoring=[{'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_RouteRefresh'}], peer=[10.0.1.3]
Received BMPRouteMonitoring=[{'path_attributes': {'ORIGIN': '?', 'MP_REACH_NLRI': {'nexthop': '192.168.105.102', 'nlri': [{'prefix': '192.168.1.0/24', 'label_list': [18], 'route_dist': '65010:101'}]}, 'AS_PATH': [], 'MULTI_EXIT_DISC': 0, 'LOCAL_PREF': 100, 'EXTENDED_COMMUNITIES': ['65010:101']}, 'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_Update'}], peer=[10.0.1.3]
Received BMPRouteMonitoring=[{'received_time': '2015/12/10 23:33:44', 'message_type': 'BGP_RouteRefresh'}], peer=[10.0.1.3]

さらに、CSR1000v上のBGPテーブルと、bgpSimulatorル上のBGPテーブルを比較してみます。

CSR1000v上のBGPテーブル

BGP1>show bgp vpnv4 unicast all
BGP table version is 68, local router ID is 10.0.1.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, 
              r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, 
              x best-external, a additional-path, c RIB-compressed, 
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

     Network          Next Hop            Metric LocPrf Weight Path
Route Distinguisher: 65010:101
 *>  172.16.0.0/24    192.168.101.101                        0 65010 ?
 * i                  192.168.104.102          0    100      0 65010 ?
 *>i 192.168.1.0      192.168.105.102          0    100      0 ?
 *>  192.168.2.0      192.168.101.101                        0 65010 ?
 * i                  192.168.104.102          0    100      0 65010 ?

bgpSimulator上のBGPテーブル

$ ./get_rib.sh

Status codes: * valid, > best
Origin codes: i - IGP, e - EGP, ? - incomplete
     Network                          Labels   Next Hop             Reason          Metric LocPrf Path
 *>  65010:101:192.168.2.0/24         [20]     192.168.101.101      ASN                           65010 ?
 *                                    [20]     192.168.104.102                      0      100    65010 ?
 *>  65010:101:172.16.0.0/24          [22]     192.168.101.101      ASN                           65010 ?
 *                                    [22]     192.168.104.102                      0      100    65010 ?
 *>  65010:101:192.168.1.0/24         [18]     192.168.105.102      Only Path       0      100    ?

CSR1000vのBGPテーブルと、bgpSimulatorのBGPテーブルは、同じBGPパス情報を保持しています。

(4) BGP Peerダウン時の動作結果の確認

今度は、BGP4〜1BGP1との間のBGP Peerがダウンさせてみます。

f:id:ttsubo:20151209222701p:plain

bgpSimulatorツールを起動したターミナル上には、BMPPeerDownNotificationを受信した旨が表示されます。

$ sudo rya-manager bgpSimulator.py

... (snip)

Received BMPPeerDownNotification, peer=[10.0.0.1]

つづいて、CSR1000v上のBGPテーブルと、bgpSimulatorル上のBGPテーブルを比較してみます。

CSR1000v上のBGPテーブル
CSR1000vのBGPテーブルでは、BGP4〜1BGP1との間のBGP Peerがダウンに基づき、BGPパス再計算が行われたため、nexthopが変化している様子がわかります。

BGP1>show bgp vpnv4 unicast all
BGP table version is 70, local router ID is 10.0.1.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, 
              r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, 
              x best-external, a additional-path, c RIB-compressed, 
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

     Network          Next Hop            Metric LocPrf Weight Path
Route Distinguisher: 65010:101
 *>i 172.16.0.0/24    192.168.104.102          0    100      0 65010 ?
 *>i 192.168.1.0      192.168.105.102          0    100      0 ?
 *>i 192.168.2.0      192.168.104.102          0    100      0 65010 ?

bgpSimulator上のBGPテーブル
一方、BGP経路監視ツール"bgpSimulator"では、”PeerDownNotification”を受信した場合ても、BGPパス再計算を実施するようには実装しておりません。
よって、 bgpSimulatorのBGPテーブルは、BGP4〜1BGP1との間のBGP Peerがダウン前のBGPパス情報からnexthopの変化は観測されません。

$ ./get_rib.sh 

Status codes: * valid, > best
Origin codes: i - IGP, e - EGP, ? - incomplete
     Network                          Labels   Next Hop             Reason          Metric LocPrf Path
 *>  65010:101:192.168.2.0/24         [20]     192.168.101.101      ASN                           65010 ?
 *                                    [20]     192.168.104.102                      0      100    65010 ?
 *>  65010:101:172.16.0.0/24          [22]     192.168.101.101      ASN                           65010 ?
 *                                    [22]     192.168.104.102                      0      100    65010 ?
 *>  65010:101:192.168.1.0/24         [18]     192.168.105.102      Only Path       0      100    ?

BGP Peerダウンが発生すると、CSR1000vのBGPテーブルと、bgpSimulatorのBGPテーブルは、同じBGPパス情報を保持しないという特徴をうまく活用してあげれば、BGP経路監視の観点で、早期にBGP障害を検知することが可能になるかもしれません。

以上より、BGP動作履歴を管理するには、BMPが有効であることが確認できました。

◾️ おまけ編:BGP経路監視ツール"bgpSimulator"をお手軽に試してみる

CSR1000vなどの実際のBGPルータを配備して、NWトポロジを構築するのは、なかなか手間がかかる話です。
RyuBGPには、BMP Client機能を有しているので、RyuBGPを活用して、BGP/MPLS-VPN面を構築した上で、もっと、手軽にBGP経路監視ツール"bgpSimulator"の動作を試してみることができるように、dockerコンテナでの実験環境を用意しました。
"README.md"通りに環境準備してもらえれば、BGP経路監視の動作イメージを体感してもらえると思います。
github.com

◾️ 終わりに

平常運用時であれば、BGP/MPLS-VPNトポロジにおけるBGP経路監視は、BMP機能が有用であることが確認できました。
BGP運用の観点でも、運用自動化ツールの適用領域を、どんどん広げていけるように、今後も精進していきたいと思います。